diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2017-07-03 14:02:27 +0200 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2017-07-03 14:02:27 +0200 |
commit | 55fe06979ceebe67553135b43aa47e70d931304b (patch) | |
tree | 16b10a744879cc1872d6c07624b59ae64469ddbf | |
parent | 56fae95f49a1ca8ca614bd9f89b0ea5f872765e9 (diff) | |
parent | 288946f1694c2abe1d2c5800c87339d1e8780e4b (diff) | |
download | sink-55fe06979ceebe67553135b43aa47e70d931304b.tar.gz sink-55fe06979ceebe67553135b43aa47e70d931304b.zip |
Merge branch 'develop'
97 files changed, 1550 insertions, 916 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 53ae5df..6690c4c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
@@ -3,10 +3,10 @@ cmake_minimum_required(VERSION 3.0) | |||
3 | cmake_policy(SET CMP0048 NEW) | 3 | cmake_policy(SET CMP0048 NEW) |
4 | cmake_policy(SET CMP0028 NEW) | 4 | cmake_policy(SET CMP0028 NEW) |
5 | 5 | ||
6 | project(sink VERSION 0.2.0) | 6 | project(sink VERSION 0.3.0) |
7 | 7 | ||
8 | option(BUILD_MAILDIR "BUILD_MAILDIR" ON) | 8 | option(BUILD_MAILDIR "BUILD_MAILDIR" ON) |
9 | option(BUILD_DAV "BUILD_DAV" OFF) | 9 | option(BUILD_DAV "BUILD_DAV" ON) |
10 | option(AVOID_BINDING_REBUILD "AVOID_BINDING_REBUILD" OFF) | 10 | option(AVOID_BINDING_REBUILD "AVOID_BINDING_REBUILD" OFF) |
11 | option(CATCH_ERRORS "CATCH_ERRORS" OFF) | 11 | option(CATCH_ERRORS "CATCH_ERRORS" OFF) |
12 | option(ENABLE_MEMCHECK "Build valgrind tests" OFF) | 12 | option(ENABLE_MEMCHECK "Build valgrind tests" OFF) |
@@ -28,7 +28,7 @@ include(KDEInstallDirs) | |||
28 | find_package(Qt5 COMPONENTS REQUIRED Core Network Gui) | 28 | find_package(Qt5 COMPONENTS REQUIRED Core Network Gui) |
29 | find_package(KF5 COMPONENTS REQUIRED Mime Contacts) | 29 | find_package(KF5 COMPONENTS REQUIRED Mime Contacts) |
30 | find_package(FlatBuffers REQUIRED 1.4.0) | 30 | find_package(FlatBuffers REQUIRED 1.4.0) |
31 | find_package(KAsync REQUIRED 0.1.0) | 31 | find_package(KAsync REQUIRED 0.1.2) |
32 | find_package(LMDB REQUIRED 0.9) | 32 | find_package(LMDB REQUIRED 0.9) |
33 | 33 | ||
34 | find_program(MEMORYCHECK_COMMAND valgrind) | 34 | find_program(MEMORYCHECK_COMMAND valgrind) |
diff --git a/common/bufferadaptor.h b/common/bufferadaptor.h index fd4809b..f393388 100644 --- a/common/bufferadaptor.h +++ b/common/bufferadaptor.h | |||
@@ -66,8 +66,7 @@ public: | |||
66 | virtual QVariant getProperty(const QByteArray &key) const | 66 | virtual QVariant getProperty(const QByteArray &key) const |
67 | { | 67 | { |
68 | if (!mValues.contains(key)) { | 68 | if (!mValues.contains(key)) { |
69 | qWarning() << "Tried to read value that is not available; Did you forget to call Query::request? Property: " << key; | 69 | return {}; |
70 | return QVariant{}; | ||
71 | } | 70 | } |
72 | return mValues.value(key); | 71 | return mValues.value(key); |
73 | } | 72 | } |
diff --git a/common/bufferutils.h b/common/bufferutils.h index 6763ced..f0460b7 100644 --- a/common/bufferutils.h +++ b/common/bufferutils.h | |||
@@ -38,7 +38,7 @@ static flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers:: | |||
38 | { | 38 | { |
39 | std::vector<flatbuffers::Offset<flatbuffers::String>> modifiedPropertiesList; | 39 | std::vector<flatbuffers::Offset<flatbuffers::String>> modifiedPropertiesList; |
40 | for (const auto &change : list) { | 40 | for (const auto &change : list) { |
41 | auto s = fbb.CreateString(change); | 41 | auto s = fbb.CreateString(change.toStdString()); |
42 | modifiedPropertiesList.push_back(s); | 42 | modifiedPropertiesList.push_back(s); |
43 | } | 43 | } |
44 | return fbb.CreateVector(modifiedPropertiesList); | 44 | return fbb.CreateVector(modifiedPropertiesList); |
diff --git a/common/changereplay.cpp b/common/changereplay.cpp index 7895b66..0adbd78 100644 --- a/common/changereplay.cpp +++ b/common/changereplay.cpp | |||
@@ -30,8 +30,7 @@ using namespace Sink; | |||
30 | using namespace Sink::Storage; | 30 | using namespace Sink::Storage; |
31 | 31 | ||
32 | ChangeReplay::ChangeReplay(const ResourceContext &resourceContext, const Sink::Log::Context &ctx) | 32 | ChangeReplay::ChangeReplay(const ResourceContext &resourceContext, const Sink::Log::Context &ctx) |
33 | : mStorage(storageLocation(), resourceContext.instanceId(), DataStore::ReadOnly), mChangeReplayStore(storageLocation(), resourceContext.instanceId() + ".changereplay", DataStore::ReadWrite), mReplayInProgress(false), mLogCtx{ctx.subContext("changereplay")}, | 33 | : mStorage(storageLocation(), resourceContext.instanceId(), DataStore::ReadOnly), mChangeReplayStore(storageLocation(), resourceContext.instanceId() + ".changereplay", DataStore::ReadWrite), mReplayInProgress(false), mLogCtx{ctx.subContext("changereplay")} |
34 | mGuard{new QObject} | ||
35 | { | 34 | { |
36 | } | 35 | } |
37 | 36 | ||
@@ -84,6 +83,8 @@ KAsync::Job<void> ChangeReplay::replayNextRevision() | |||
84 | auto replayStoreTransaction = mChangeReplayStore.createTransaction(DataStore::ReadOnly, [this](const DataStore::Error &error) { | 83 | auto replayStoreTransaction = mChangeReplayStore.createTransaction(DataStore::ReadOnly, [this](const DataStore::Error &error) { |
85 | SinkWarningCtx(mLogCtx) << error.message; | 84 | SinkWarningCtx(mLogCtx) << error.message; |
86 | }); | 85 | }); |
86 | Q_ASSERT(mMainStoreTransaction); | ||
87 | Q_ASSERT(replayStoreTransaction); | ||
87 | replayStoreTransaction.openDatabase().scan("lastReplayedRevision", | 88 | replayStoreTransaction.openDatabase().scan("lastReplayedRevision", |
88 | [lastReplayedRevision](const QByteArray &key, const QByteArray &value) -> bool { | 89 | [lastReplayedRevision](const QByteArray &key, const QByteArray &value) -> bool { |
89 | *lastReplayedRevision = value.toLongLong(); | 90 | *lastReplayedRevision = value.toLongLong(); |
@@ -98,16 +99,13 @@ KAsync::Job<void> ChangeReplay::replayNextRevision() | |||
98 | SinkTraceCtx(mLogCtx) << "Changereplay from " << *lastReplayedRevision << " to " << *topRevision; | 99 | SinkTraceCtx(mLogCtx) << "Changereplay from " << *lastReplayedRevision << " to " << *topRevision; |
99 | return KAsync::doWhile( | 100 | return KAsync::doWhile( |
100 | [this, lastReplayedRevision, topRevision]() -> KAsync::Job<KAsync::ControlFlowFlag> { | 101 | [this, lastReplayedRevision, topRevision]() -> KAsync::Job<KAsync::ControlFlowFlag> { |
101 | if (!mGuard) { | ||
102 | SinkTraceCtx(mLogCtx) << "Exit due to guard"; | ||
103 | return KAsync::value(KAsync::Break); | ||
104 | } | ||
105 | if (*lastReplayedRevision >= *topRevision) { | 102 | if (*lastReplayedRevision >= *topRevision) { |
106 | SinkTraceCtx(mLogCtx) << "Done replaying" << *lastReplayedRevision << *topRevision; | 103 | SinkTraceCtx(mLogCtx) << "Done replaying" << *lastReplayedRevision << *topRevision; |
107 | return KAsync::value(KAsync::Break); | 104 | return KAsync::value(KAsync::Break); |
108 | } | 105 | } |
106 | Q_ASSERT(mMainStoreTransaction); | ||
109 | 107 | ||
110 | KAsync::Job<void> replayJob = KAsync::null<void>(); | 108 | auto replayJob = KAsync::null(); |
111 | qint64 revision = *lastReplayedRevision + 1; | 109 | qint64 revision = *lastReplayedRevision + 1; |
112 | while (revision <= *topRevision) { | 110 | while (revision <= *topRevision) { |
113 | const auto uid = DataStore::getUidFromRevision(mMainStoreTransaction, revision); | 111 | const auto uid = DataStore::getUidFromRevision(mMainStoreTransaction, revision); |
@@ -131,14 +129,14 @@ KAsync::Job<void> ChangeReplay::replayNextRevision() | |||
131 | if (canReplay(type, key, entityBuffer)) { | 129 | if (canReplay(type, key, entityBuffer)) { |
132 | SinkTraceCtx(mLogCtx) << "Replaying " << key; | 130 | SinkTraceCtx(mLogCtx) << "Replaying " << key; |
133 | replayJob = replay(type, key, entityBuffer); | 131 | replayJob = replay(type, key, entityBuffer); |
132 | //Set the last revision we tried to replay | ||
133 | *lastReplayedRevision = revision; | ||
134 | //Execute replay job and commit | ||
135 | break; | ||
134 | } else { | 136 | } else { |
135 | SinkTraceCtx(mLogCtx) << "Cannot replay " << key; | 137 | SinkTraceCtx(mLogCtx) << "Not replaying " << key; |
136 | //We silently skip over revisions that cannot be replayed, as this is not an error. | 138 | //We silently skip over revisions that cannot be replayed, as this is not an error. |
137 | replayJob = KAsync::null(); | ||
138 | } | 139 | } |
139 | //Set the last revision we tried to replay | ||
140 | *lastReplayedRevision = revision; | ||
141 | break; | ||
142 | } | 140 | } |
143 | } | 141 | } |
144 | //Bump the revision if we failed to even attempt to replay. This will simply skip over those revisions, as we can't recover from those situations. | 142 | //Bump the revision if we failed to even attempt to replay. This will simply skip over those revisions, as we can't recover from those situations. |
@@ -149,22 +147,22 @@ KAsync::Job<void> ChangeReplay::replayNextRevision() | |||
149 | if (error) { | 147 | if (error) { |
150 | SinkWarningCtx(mLogCtx) << "Change replay failed: " << error << "Last replayed revision: " << *lastReplayedRevision; | 148 | SinkWarningCtx(mLogCtx) << "Change replay failed: " << error << "Last replayed revision: " << *lastReplayedRevision; |
151 | //We're probably not online or so, so postpone retrying | 149 | //We're probably not online or so, so postpone retrying |
152 | return KAsync::value(KAsync::Break); | 150 | return KAsync::value(KAsync::Break).then(KAsync::error<KAsync::ControlFlowFlag>(error)); |
151 | } | ||
152 | SinkTraceCtx(mLogCtx) << "Replayed until: " << *lastReplayedRevision; | ||
153 | |||
154 | recordReplayedRevision(*lastReplayedRevision); | ||
155 | reportProgress(*lastReplayedRevision, *topRevision); | ||
156 | |||
157 | const bool gotMoreToReplay = (*lastReplayedRevision < *topRevision); | ||
158 | if (gotMoreToReplay) { | ||
159 | SinkTraceCtx(mLogCtx) << "Replaying some more..."; | ||
160 | //Replay more if we have more | ||
161 | return KAsync::wait(0).then(KAsync::value(KAsync::Continue)); | ||
153 | } else { | 162 | } else { |
154 | SinkTraceCtx(mLogCtx) << "Replayed until: " << *lastReplayedRevision; | 163 | return KAsync::value(KAsync::Break); |
155 | recordReplayedRevision(*lastReplayedRevision); | ||
156 | if (*lastReplayedRevision < *topRevision) { | ||
157 | SinkTraceCtx(mLogCtx) << "Replaying some more..."; | ||
158 | //Replay more if we have more | ||
159 | return KAsync::wait(0).then(KAsync::value(KAsync::Continue)); | ||
160 | } else { | ||
161 | return KAsync::value(KAsync::Break); | ||
162 | } | ||
163 | } | 164 | } |
164 | //We shouldn't ever get here | 165 | }).guard(&mGuard); |
165 | Q_ASSERT(false); | ||
166 | return KAsync::value(KAsync::Break); | ||
167 | }); | ||
168 | }); | 166 | }); |
169 | }) | 167 | }) |
170 | .then([this](const KAsync::Error &error) { | 168 | .then([this](const KAsync::Error &error) { |
@@ -181,7 +179,12 @@ KAsync::Job<void> ChangeReplay::replayNextRevision() | |||
181 | emit changesReplayed(); | 179 | emit changesReplayed(); |
182 | } | 180 | } |
183 | } | 181 | } |
184 | }); | 182 | if (error) { |
183 | return KAsync::error(error); | ||
184 | } else { | ||
185 | return KAsync::null(); | ||
186 | } | ||
187 | }).guard(&mGuard); | ||
185 | } | 188 | } |
186 | 189 | ||
187 | void ChangeReplay::revisionChanged() | 190 | void ChangeReplay::revisionChanged() |
diff --git a/common/changereplay.h b/common/changereplay.h index edc4462..22e26a5 100644 --- a/common/changereplay.h +++ b/common/changereplay.h | |||
@@ -54,6 +54,7 @@ public slots: | |||
54 | protected: | 54 | protected: |
55 | virtual KAsync::Job<void> replay(const QByteArray &type, const QByteArray &key, const QByteArray &value) = 0; | 55 | virtual KAsync::Job<void> replay(const QByteArray &type, const QByteArray &key, const QByteArray &value) = 0; |
56 | virtual bool canReplay(const QByteArray &type, const QByteArray &key, const QByteArray &value) = 0; | 56 | virtual bool canReplay(const QByteArray &type, const QByteArray &key, const QByteArray &value) = 0; |
57 | virtual void reportProgress(int progress, int total, const QByteArrayList &applicableEntities = {}){}; | ||
57 | Sink::Storage::DataStore mStorage; | 58 | Sink::Storage::DataStore mStorage; |
58 | KAsync::Job<void> replayNextRevision(); | 59 | KAsync::Job<void> replayNextRevision(); |
59 | 60 | ||
@@ -63,7 +64,7 @@ private: | |||
63 | bool mReplayInProgress; | 64 | bool mReplayInProgress; |
64 | Sink::Storage::DataStore::Transaction mMainStoreTransaction; | 65 | Sink::Storage::DataStore::Transaction mMainStoreTransaction; |
65 | Sink::Log::Context mLogCtx; | 66 | Sink::Log::Context mLogCtx; |
66 | QSharedPointer<QObject> mGuard; | 67 | QObject mGuard; |
67 | }; | 68 | }; |
68 | 69 | ||
69 | class NullChangeReplay : public ChangeReplay | 70 | class NullChangeReplay : public ChangeReplay |
diff --git a/common/commands.cpp b/common/commands.cpp index eeb7f08..24f2017 100644 --- a/common/commands.cpp +++ b/common/commands.cpp | |||
@@ -1,5 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org> | 2 | * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org> |
3 | * Copyright (C) 2016 Christian Mollekopf <mollekopf@kolabsys.com> | ||
3 | * | 4 | * |
4 | * This library is free software; you can redistribute it and/or | 5 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public | 6 | * modify it under the terms of the GNU Lesser General Public |
@@ -20,7 +21,7 @@ | |||
20 | 21 | ||
21 | #include "commands.h" | 22 | #include "commands.h" |
22 | 23 | ||
23 | #include <QIODevice> | 24 | #include <QLocalSocket> |
24 | #include <log.h> | 25 | #include <log.h> |
25 | 26 | ||
26 | namespace Sink { | 27 | namespace Sink { |
@@ -73,19 +74,19 @@ int headerSize() | |||
73 | return sizeof(int) + (sizeof(uint) * 2); | 74 | return sizeof(int) + (sizeof(uint) * 2); |
74 | } | 75 | } |
75 | 76 | ||
76 | void write(QIODevice *device, int messageId, int commandId) | 77 | void write(QLocalSocket *device, int messageId, int commandId) |
77 | { | 78 | { |
78 | write(device, messageId, commandId, 0, 0); | 79 | write(device, messageId, commandId, 0, 0); |
79 | } | 80 | } |
80 | 81 | ||
81 | static void write(QIODevice *device, const char *buffer, uint size) | 82 | static void write(QLocalSocket *device, const char *buffer, uint size) |
82 | { | 83 | { |
83 | if (device->write(buffer, size) < 0) { | 84 | if (device->write(buffer, size) < 0) { |
84 | SinkWarningCtx(Sink::Log::Context{"commands"}) << "Error while writing " << device->errorString(); | 85 | SinkWarningCtx(Sink::Log::Context{"commands"}) << "Error while writing " << device->errorString(); |
85 | } | 86 | } |
86 | } | 87 | } |
87 | 88 | ||
88 | void write(QIODevice *device, int messageId, int commandId, const char *buffer, uint size) | 89 | void write(QLocalSocket *device, int messageId, int commandId, const char *buffer, uint size) |
89 | { | 90 | { |
90 | if (size > 0 && !buffer) { | 91 | if (size > 0 && !buffer) { |
91 | size = 0; | 92 | size = 0; |
@@ -97,15 +98,16 @@ void write(QIODevice *device, int messageId, int commandId, const char *buffer, | |||
97 | if (buffer) { | 98 | if (buffer) { |
98 | write(device, buffer, size); | 99 | write(device, buffer, size); |
99 | } | 100 | } |
101 | //The default implementation will happily buffer 200k bytes before sending it out which doesn't make the sytem exactly responsive. | ||
102 | //1k is arbitrary, but fits a bunch of messages at least. | ||
103 | if (device->bytesToWrite() > 1000) { | ||
104 | device->flush(); | ||
105 | } | ||
100 | } | 106 | } |
101 | 107 | ||
102 | void write(QIODevice *device, int messageId, int commandId, flatbuffers::FlatBufferBuilder &fbb) | 108 | void write(QLocalSocket *device, int messageId, int commandId, flatbuffers::FlatBufferBuilder &fbb) |
103 | { | 109 | { |
104 | const int dataSize = fbb.GetSize(); | 110 | write(device, messageId, commandId, (const char *)fbb.GetBufferPointer(), fbb.GetSize()); |
105 | write(device, (const char *)&messageId, sizeof(int)); | ||
106 | write(device, (const char *)&commandId, sizeof(int)); | ||
107 | write(device, (const char *)&dataSize, sizeof(int)); | ||
108 | write(device, (const char *)fbb.GetBufferPointer(), dataSize); | ||
109 | } | 111 | } |
110 | 112 | ||
111 | } // namespace Commands | 113 | } // namespace Commands |
diff --git a/common/commands.h b/common/commands.h index 6d5d39b..1548eac 100644 --- a/common/commands.h +++ b/common/commands.h | |||
@@ -24,7 +24,7 @@ | |||
24 | #include <flatbuffers/flatbuffers.h> | 24 | #include <flatbuffers/flatbuffers.h> |
25 | #include <QByteArray> | 25 | #include <QByteArray> |
26 | 26 | ||
27 | class QIODevice; | 27 | class QLocalSocket; |
28 | 28 | ||
29 | namespace Sink { | 29 | namespace Sink { |
30 | 30 | ||
@@ -55,9 +55,9 @@ enum CommandIds | |||
55 | QByteArray name(int commandId); | 55 | QByteArray name(int commandId); |
56 | 56 | ||
57 | int SINK_EXPORT headerSize(); | 57 | int SINK_EXPORT headerSize(); |
58 | void SINK_EXPORT write(QIODevice *device, int messageId, int commandId); | 58 | void SINK_EXPORT write(QLocalSocket *device, int messageId, int commandId); |
59 | void SINK_EXPORT write(QIODevice *device, int messageId, int commandId, const char *buffer, uint size); | 59 | void SINK_EXPORT write(QLocalSocket *device, int messageId, int commandId, const char *buffer, uint size); |
60 | void SINK_EXPORT write(QIODevice *device, int messageId, int commandId, flatbuffers::FlatBufferBuilder &fbb); | 60 | void SINK_EXPORT write(QLocalSocket *device, int messageId, int commandId, flatbuffers::FlatBufferBuilder &fbb); |
61 | } | 61 | } |
62 | 62 | ||
63 | } // namespace Sink | 63 | } // namespace Sink |
diff --git a/common/commands/notification.fbs b/common/commands/notification.fbs index 517111c..7ced666 100644 --- a/common/commands/notification.fbs +++ b/common/commands/notification.fbs | |||
@@ -5,6 +5,8 @@ table Notification { | |||
5 | identifier: string; //An identifier that links back to the something related to the notification (e.g. a command id) | 5 | identifier: string; //An identifier that links back to the something related to the notification (e.g. a command id) |
6 | message: string; | 6 | message: string; |
7 | code: int = 0; //See notification.h | 7 | code: int = 0; //See notification.h |
8 | progress: int = 0; //See notification.h | ||
9 | total: int = 0; //See notification.h | ||
8 | entities: [string]; //A list of entities this applies to | 10 | entities: [string]; //A list of entities this applies to |
9 | } | 11 | } |
10 | 12 | ||
diff --git a/common/configstore.cpp b/common/configstore.cpp index 1ae9da8..6751907 100644 --- a/common/configstore.cpp +++ b/common/configstore.cpp | |||
@@ -24,8 +24,6 @@ | |||
24 | #include <log.h> | 24 | #include <log.h> |
25 | #include <definitions.h> | 25 | #include <definitions.h> |
26 | 26 | ||
27 | SINK_DEBUG_AREA("configstore") | ||
28 | |||
29 | static QSharedPointer<QSettings> getConfig(const QByteArray &identifier) | 27 | static QSharedPointer<QSettings> getConfig(const QByteArray &identifier) |
30 | { | 28 | { |
31 | return QSharedPointer<QSettings>::create(Sink::configLocation() + "/" + identifier + ".ini", QSettings::IniFormat); | 29 | return QSharedPointer<QSettings>::create(Sink::configLocation() + "/" + identifier + ".ini", QSettings::IniFormat); |
diff --git a/common/contactpreprocessor.cpp b/common/contactpreprocessor.cpp index ac2c3bc..d331421 100644 --- a/common/contactpreprocessor.cpp +++ b/common/contactpreprocessor.cpp | |||
@@ -35,6 +35,8 @@ void updatedProperties(Sink::ApplicationDomain::Contact &contact, const KContact | |||
35 | emails << Sink::ApplicationDomain::Contact::Email{Sink::ApplicationDomain::Contact::Email::Undefined, email}; | 35 | emails << Sink::ApplicationDomain::Contact::Email{Sink::ApplicationDomain::Contact::Email::Undefined, email}; |
36 | } | 36 | } |
37 | contact.setEmails(emails); | 37 | contact.setEmails(emails); |
38 | |||
39 | contact.setPhoto(addressee.photo().rawData()); | ||
38 | } | 40 | } |
39 | 41 | ||
40 | ContactPropertyExtractor::~ContactPropertyExtractor() | 42 | ContactPropertyExtractor::~ContactPropertyExtractor() |
diff --git a/common/datastorequery.cpp b/common/datastorequery.cpp index 2e0c348..4c95606 100644 --- a/common/datastorequery.cpp +++ b/common/datastorequery.cpp | |||
@@ -43,6 +43,8 @@ class Source : public FilterBase { | |||
43 | 43 | ||
44 | QVector<QByteArray> mIds; | 44 | QVector<QByteArray> mIds; |
45 | QVector<QByteArray>::ConstIterator mIt; | 45 | QVector<QByteArray>::ConstIterator mIt; |
46 | QVector<QByteArray> mIncrementalIds; | ||
47 | QVector<QByteArray>::ConstIterator mIncrementalIt; | ||
46 | 48 | ||
47 | Source (const QVector<QByteArray> &ids, DataStoreQuery *store) | 49 | Source (const QVector<QByteArray> &ids, DataStoreQuery *store) |
48 | : FilterBase(store), | 50 | : FilterBase(store), |
@@ -63,21 +65,36 @@ class Source : public FilterBase { | |||
63 | 65 | ||
64 | void add(const QVector<QByteArray> &ids) | 66 | void add(const QVector<QByteArray> &ids) |
65 | { | 67 | { |
66 | mIds = ids; | 68 | mIncrementalIds = ids; |
67 | mIt = mIds.constBegin(); | 69 | mIncrementalIt = mIncrementalIds.constBegin(); |
68 | } | 70 | } |
69 | 71 | ||
70 | bool next(const std::function<void(const ResultSet::Result &result)> &callback) Q_DECL_OVERRIDE | 72 | bool next(const std::function<void(const ResultSet::Result &result)> &callback) Q_DECL_OVERRIDE |
71 | { | 73 | { |
72 | if (mIt == mIds.constEnd()) { | 74 | if (!mIncrementalIds.isEmpty()) { |
73 | return false; | 75 | if (mIncrementalIt == mIncrementalIds.constEnd()) { |
76 | return false; | ||
77 | } | ||
78 | readEntity(*mIncrementalIt, [this, callback](const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Operation operation) { | ||
79 | SinkTraceCtx(mDatastore->mLogCtx) << "Source: Read entity: " << entity.identifier() << operationName(operation); | ||
80 | callback({entity, operation}); | ||
81 | }); | ||
82 | mIncrementalIt++; | ||
83 | if (mIncrementalIt == mIncrementalIds.constEnd()) { | ||
84 | return false; | ||
85 | } | ||
86 | return true; | ||
87 | } else { | ||
88 | if (mIt == mIds.constEnd()) { | ||
89 | return false; | ||
90 | } | ||
91 | readEntity(*mIt, [this, callback](const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Operation operation) { | ||
92 | SinkTraceCtx(mDatastore->mLogCtx) << "Source: Read entity: " << entity.identifier() << operationName(operation); | ||
93 | callback({entity, operation}); | ||
94 | }); | ||
95 | mIt++; | ||
96 | return mIt != mIds.constEnd(); | ||
74 | } | 97 | } |
75 | readEntity(*mIt, [this, callback](const Sink::ApplicationDomain::ApplicationDomainType &entity, Sink::Operation operation) { | ||
76 | SinkTraceCtx(mDatastore->mLogCtx) << "Source: Read entity: " << entity.identifier() << operationName(operation); | ||
77 | callback({entity, operation}); | ||
78 | }); | ||
79 | mIt++; | ||
80 | return mIt != mIds.constEnd(); | ||
81 | } | 98 | } |
82 | }; | 99 | }; |
83 | 100 | ||
@@ -599,6 +616,10 @@ ResultSet DataStoreQuery::update(qint64 baseRevision) | |||
599 | return ResultSet(generator, [this]() { mCollector->skip(); }); | 616 | return ResultSet(generator, [this]() { mCollector->skip(); }); |
600 | } | 617 | } |
601 | 618 | ||
619 | void DataStoreQuery::updateComplete() | ||
620 | { | ||
621 | mSource->mIncrementalIds.clear(); | ||
622 | } | ||
602 | 623 | ||
603 | ResultSet DataStoreQuery::execute() | 624 | ResultSet DataStoreQuery::execute() |
604 | { | 625 | { |
diff --git a/common/datastorequery.h b/common/datastorequery.h index ee5f99e..de4ae26 100644 --- a/common/datastorequery.h +++ b/common/datastorequery.h | |||
@@ -50,6 +50,7 @@ public: | |||
50 | ~DataStoreQuery(); | 50 | ~DataStoreQuery(); |
51 | ResultSet execute(); | 51 | ResultSet execute(); |
52 | ResultSet update(qint64 baseRevision); | 52 | ResultSet update(qint64 baseRevision); |
53 | void updateComplete(); | ||
53 | 54 | ||
54 | State::Ptr getState(); | 55 | State::Ptr getState(); |
55 | 56 | ||
diff --git a/common/definitions.cpp b/common/definitions.cpp index 3fc4700..17977bc 100644 --- a/common/definitions.cpp +++ b/common/definitions.cpp | |||
@@ -23,6 +23,17 @@ | |||
23 | #include <QStandardPaths> | 23 | #include <QStandardPaths> |
24 | #include <QDir> | 24 | #include <QDir> |
25 | 25 | ||
26 | static bool rereadDataLocation = true; | ||
27 | static bool rereadConfigLocation = true; | ||
28 | static bool rereadTemporaryFileLocation = true; | ||
29 | |||
30 | void Sink::clearLocationCache() | ||
31 | { | ||
32 | rereadDataLocation = true; | ||
33 | rereadConfigLocation = true; | ||
34 | rereadTemporaryFileLocation = true; | ||
35 | } | ||
36 | |||
26 | QString Sink::storageLocation() | 37 | QString Sink::storageLocation() |
27 | { | 38 | { |
28 | return dataLocation() + "/storage"; | 39 | return dataLocation() + "/storage"; |
@@ -30,21 +41,37 @@ QString Sink::storageLocation() | |||
30 | 41 | ||
31 | QString Sink::dataLocation() | 42 | QString Sink::dataLocation() |
32 | { | 43 | { |
33 | return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/sink"; | 44 | static QString location; |
45 | if (rereadDataLocation) { | ||
46 | location = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/sink"; | ||
47 | rereadDataLocation = false; | ||
48 | } | ||
49 | return location; | ||
34 | } | 50 | } |
35 | 51 | ||
36 | QString Sink::configLocation() | 52 | QString Sink::configLocation() |
37 | { | 53 | { |
38 | return QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/sink"; | 54 | static QString location; |
55 | if (rereadConfigLocation) { | ||
56 | location = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/sink"; | ||
57 | rereadConfigLocation = false; | ||
58 | } | ||
59 | return location; | ||
39 | } | 60 | } |
40 | 61 | ||
41 | QString Sink::temporaryFileLocation() | 62 | QString Sink::temporaryFileLocation() |
42 | { | 63 | { |
43 | auto path = dataLocation() + "/temporaryFiles"; | 64 | static QString location; |
44 | //FIXME create in a singleton on startup? | 65 | static bool dirCreated = false; |
45 | QDir dir; | 66 | if (rereadTemporaryFileLocation) { |
46 | dir.mkpath(path); | 67 | location = dataLocation() + "/temporaryFiles"; |
47 | return path; | 68 | dirCreated = QDir{}.mkpath(location); |
69 | rereadTemporaryFileLocation = false; | ||
70 | } | ||
71 | if (!dirCreated && QDir{}.mkpath(location)) { | ||
72 | dirCreated = true; | ||
73 | } | ||
74 | return location; | ||
48 | } | 75 | } |
49 | 76 | ||
50 | QString Sink::resourceStorageLocation(const QByteArray &resourceInstanceIdentifier) | 77 | QString Sink::resourceStorageLocation(const QByteArray &resourceInstanceIdentifier) |
diff --git a/common/definitions.h b/common/definitions.h index e8cd45e..ce9e794 100644 --- a/common/definitions.h +++ b/common/definitions.h | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <QByteArray> | 25 | #include <QByteArray> |
26 | 26 | ||
27 | namespace Sink { | 27 | namespace Sink { |
28 | void SINK_EXPORT clearLocationCache(); | ||
28 | QString SINK_EXPORT storageLocation(); | 29 | QString SINK_EXPORT storageLocation(); |
29 | QString SINK_EXPORT dataLocation(); | 30 | QString SINK_EXPORT dataLocation(); |
30 | QString SINK_EXPORT configLocation(); | 31 | QString SINK_EXPORT configLocation(); |
diff --git a/common/domain/applicationdomaintype.cpp b/common/domain/applicationdomaintype.cpp index 67d463f..3718f77 100644 --- a/common/domain/applicationdomaintype.cpp +++ b/common/domain/applicationdomaintype.cpp | |||
@@ -25,8 +25,6 @@ | |||
25 | #include "storage.h" //for generateUid() | 25 | #include "storage.h" //for generateUid() |
26 | #include <QFile> | 26 | #include <QFile> |
27 | 27 | ||
28 | SINK_DEBUG_AREA("applicationdomaintype"); | ||
29 | |||
30 | QDebug Sink::ApplicationDomain::operator<< (QDebug d, const Sink::ApplicationDomain::Mail::Contact &c) | 28 | QDebug Sink::ApplicationDomain::operator<< (QDebug d, const Sink::ApplicationDomain::Mail::Contact &c) |
31 | { | 29 | { |
32 | d << "Contact(" << c.name << ", " << c.emailAddress << ")"; | 30 | d << "Contact(" << c.name << ", " << c.emailAddress << ")"; |
@@ -114,6 +112,7 @@ SINK_REGISTER_PROPERTY(Contact, Lastname); | |||
114 | SINK_REGISTER_PROPERTY(Contact, Emails); | 112 | SINK_REGISTER_PROPERTY(Contact, Emails); |
115 | SINK_REGISTER_PROPERTY(Contact, Vcard); | 113 | SINK_REGISTER_PROPERTY(Contact, Vcard); |
116 | SINK_REGISTER_PROPERTY(Contact, Addressbook); | 114 | SINK_REGISTER_PROPERTY(Contact, Addressbook); |
115 | SINK_REGISTER_PROPERTY(Contact, Photo); | ||
117 | 116 | ||
118 | SINK_REGISTER_ENTITY(Addressbook); | 117 | SINK_REGISTER_ENTITY(Addressbook); |
119 | SINK_REGISTER_PROPERTY(Addressbook, Name); | 118 | SINK_REGISTER_PROPERTY(Addressbook, Name); |
diff --git a/common/domain/applicationdomaintype.h b/common/domain/applicationdomaintype.h index e5aa46e..602d54c 100644 --- a/common/domain/applicationdomaintype.h +++ b/common/domain/applicationdomaintype.h | |||
@@ -377,6 +377,7 @@ struct SINK_EXPORT Contact : public Entity { | |||
377 | SINK_PROPERTY(QString, Lastname, lastname); | 377 | SINK_PROPERTY(QString, Lastname, lastname); |
378 | SINK_PROPERTY(QList<Email>, Emails, emails); | 378 | SINK_PROPERTY(QList<Email>, Emails, emails); |
379 | SINK_PROPERTY(QByteArray, Vcard, vcard); | 379 | SINK_PROPERTY(QByteArray, Vcard, vcard); |
380 | SINK_PROPERTY(QByteArray, Photo, photo); | ||
380 | SINK_REFERENCE_PROPERTY(Addressbook, Addressbook, addressbook); | 381 | SINK_REFERENCE_PROPERTY(Addressbook, Addressbook, addressbook); |
381 | }; | 382 | }; |
382 | 383 | ||
diff --git a/common/domain/contact.fbs b/common/domain/contact.fbs index d941d5a..e689756 100644 --- a/common/domain/contact.fbs +++ b/common/domain/contact.fbs | |||
@@ -13,6 +13,7 @@ table Contact { | |||
13 | addressbook:string; | 13 | addressbook:string; |
14 | emails: [ContactEmail]; | 14 | emails: [ContactEmail]; |
15 | vcard: string; | 15 | vcard: string; |
16 | photo: string; | ||
16 | } | 17 | } |
17 | 18 | ||
18 | root_type Contact; | 19 | root_type Contact; |
diff --git a/common/domain/typeimplementations.cpp b/common/domain/typeimplementations.cpp index eb3851e..47a9cf7 100644 --- a/common/domain/typeimplementations.cpp +++ b/common/domain/typeimplementations.cpp | |||
@@ -28,25 +28,52 @@ | |||
28 | #include "entity_generated.h" | 28 | #include "entity_generated.h" |
29 | #include "mail/threadindexer.h" | 29 | #include "mail/threadindexer.h" |
30 | #include "domainadaptor.h" | 30 | #include "domainadaptor.h" |
31 | #include "typeimplementations_p.h" | ||
31 | 32 | ||
32 | using namespace Sink; | 33 | using namespace Sink; |
33 | using namespace Sink::ApplicationDomain; | 34 | using namespace Sink::ApplicationDomain; |
34 | 35 | ||
36 | #define SINK_REGISTER_SERIALIZER(MAPPER, ENTITYTYPE, PROPERTY, LOWERCASEPROPERTY) \ | ||
37 | MAPPER.addMapping<ENTITYTYPE::PROPERTY, Sink::ApplicationDomain::Buffer::ENTITYTYPE, Sink::ApplicationDomain::Buffer::ENTITYTYPE##Builder>(&Sink::ApplicationDomain::Buffer::ENTITYTYPE::LOWERCASEPROPERTY, &Sink::ApplicationDomain::Buffer::ENTITYTYPE##Builder::add_##LOWERCASEPROPERTY); | ||
38 | |||
39 | typedef IndexConfig<Mail, | ||
40 | ValueIndex<Mail::Date>, | ||
41 | ValueIndex<Mail::Folder>, | ||
42 | ValueIndex<Mail::ParentMessageId>, | ||
43 | ValueIndex<Mail::MessageId>, | ||
44 | ValueIndex<Mail::Draft>, | ||
45 | SortedIndex<Mail::Folder, Mail::Date>, | ||
46 | SecondaryIndex<Mail::MessageId, Mail::ThreadId>, | ||
47 | SecondaryIndex<Mail::ThreadId, Mail::MessageId>, | ||
48 | CustomSecondaryIndex<Mail::MessageId, Mail::ThreadId, ThreadIndexer> | ||
49 | > MailIndexConfig; | ||
50 | |||
51 | typedef IndexConfig<Folder, | ||
52 | ValueIndex<Folder::Name>, | ||
53 | ValueIndex<Folder::Parent> | ||
54 | > FolderIndexConfig; | ||
55 | |||
56 | typedef IndexConfig<Contact, | ||
57 | ValueIndex<Contact::Uid> | ||
58 | > ContactIndexConfig; | ||
59 | |||
60 | typedef IndexConfig<Addressbook, | ||
61 | ValueIndex<Addressbook::Parent> | ||
62 | > AddressbookIndexConfig; | ||
63 | |||
64 | typedef IndexConfig<Event, | ||
65 | ValueIndex<Event::Uid> | ||
66 | > EventIndexConfig; | ||
67 | |||
68 | |||
35 | void TypeImplementation<Mail>::configure(TypeIndex &index) | 69 | void TypeImplementation<Mail>::configure(TypeIndex &index) |
36 | { | 70 | { |
37 | // index.addProperty<Mail::Sender>(); | 71 | MailIndexConfig::configure(index); |
38 | /* index.addProperty<QByteArray>(Mail::SenderName::name); */ | 72 | } |
39 | /* index->addProperty<QString>(Mail::Subject::name); */ | ||
40 | /* index->addFulltextProperty<QString>(Mail::Subject::name); */ | ||
41 | index.addProperty<Mail::Date>(); | ||
42 | index.addProperty<Mail::Folder>(); | ||
43 | index.addPropertyWithSorting<Mail::Folder, Mail::Date>(); | ||
44 | index.addProperty<Mail::ParentMessageId>(); | ||
45 | index.addProperty<Mail::MessageId>(); | ||
46 | 73 | ||
47 | index.addSecondaryPropertyIndexer<Mail::MessageId, Mail::ThreadId, ThreadIndexer>(); | 74 | QMap<QByteArray, int> TypeImplementation<Mail>::typeDatabases() |
48 | index.addSecondaryProperty<Mail::MessageId, Mail::ThreadId>(); | 75 | { |
49 | index.addSecondaryProperty<Mail::ThreadId, Mail::MessageId>(); | 76 | return merge(QMap<QByteArray, int>{{QByteArray{Mail::name} + ".main", 0}}, MailIndexConfig::databases()); |
50 | } | 77 | } |
51 | 78 | ||
52 | void TypeImplementation<Mail>::configure(IndexPropertyMapper &indexPropertyMapper) | 79 | void TypeImplementation<Mail>::configure(IndexPropertyMapper &indexPropertyMapper) |
@@ -61,69 +88,44 @@ void TypeImplementation<Mail>::configure(IndexPropertyMapper &indexPropertyMappe | |||
61 | }); | 88 | }); |
62 | } | 89 | } |
63 | 90 | ||
64 | void TypeImplementation<Mail>::configure(ReadPropertyMapper<Buffer> &propertyMapper) | 91 | void TypeImplementation<Mail>::configure(PropertyMapper &propertyMapper) |
65 | { | 92 | { |
66 | propertyMapper.addMapping<Mail::Sender, Buffer>(&Buffer::sender); | 93 | SINK_REGISTER_SERIALIZER(propertyMapper, Mail, Sender, sender); |
67 | propertyMapper.addMapping<Mail::To, Buffer>(&Buffer::to); | 94 | SINK_REGISTER_SERIALIZER(propertyMapper, Mail, To, to); |
68 | propertyMapper.addMapping<Mail::Cc, Buffer>(&Buffer::cc); | 95 | SINK_REGISTER_SERIALIZER(propertyMapper, Mail, Cc, cc); |
69 | propertyMapper.addMapping<Mail::Bcc, Buffer>(&Buffer::bcc); | 96 | SINK_REGISTER_SERIALIZER(propertyMapper, Mail, Bcc, bcc); |
70 | propertyMapper.addMapping<Mail::Subject, Buffer>(&Buffer::subject); | 97 | SINK_REGISTER_SERIALIZER(propertyMapper, Mail, Subject, subject); |
71 | propertyMapper.addMapping<Mail::Date, Buffer>(&Buffer::date); | 98 | SINK_REGISTER_SERIALIZER(propertyMapper, Mail, Date, date); |
72 | propertyMapper.addMapping<Mail::Unread, Buffer>(&Buffer::unread); | 99 | SINK_REGISTER_SERIALIZER(propertyMapper, Mail, Unread, unread); |
73 | propertyMapper.addMapping<Mail::Important, Buffer>(&Buffer::important); | 100 | SINK_REGISTER_SERIALIZER(propertyMapper, Mail, Important, important); |
74 | propertyMapper.addMapping<Mail::Folder, Buffer>(&Buffer::folder); | 101 | SINK_REGISTER_SERIALIZER(propertyMapper, Mail, Folder, folder); |
75 | propertyMapper.addMapping<Mail::MimeMessage, Buffer>(&Buffer::mimeMessage); | 102 | SINK_REGISTER_SERIALIZER(propertyMapper, Mail, MimeMessage, mimeMessage); |
76 | propertyMapper.addMapping<Mail::FullPayloadAvailable, Buffer>(&Buffer::fullPayloadAvailable); | 103 | SINK_REGISTER_SERIALIZER(propertyMapper, Mail, FullPayloadAvailable, fullPayloadAvailable); |
77 | propertyMapper.addMapping<Mail::Draft, Buffer>(&Buffer::draft); | 104 | SINK_REGISTER_SERIALIZER(propertyMapper, Mail, Draft, draft); |
78 | propertyMapper.addMapping<Mail::Trash, Buffer>(&Buffer::trash); | 105 | SINK_REGISTER_SERIALIZER(propertyMapper, Mail, Trash, trash); |
79 | propertyMapper.addMapping<Mail::Sent, Buffer>(&Buffer::sent); | 106 | SINK_REGISTER_SERIALIZER(propertyMapper, Mail, Sent, sent); |
80 | propertyMapper.addMapping<Mail::MessageId, Buffer>(&Buffer::messageId); | 107 | SINK_REGISTER_SERIALIZER(propertyMapper, Mail, MessageId, messageId); |
81 | propertyMapper.addMapping<Mail::ParentMessageId, Buffer>(&Buffer::parentMessageId); | 108 | SINK_REGISTER_SERIALIZER(propertyMapper, Mail, ParentMessageId, parentMessageId); |
82 | } | ||
83 | |||
84 | void TypeImplementation<Mail>::configure(WritePropertyMapper<BufferBuilder> &propertyMapper) | ||
85 | { | ||
86 | propertyMapper.addMapping<Mail::Sender>(&BufferBuilder::add_sender); | ||
87 | propertyMapper.addMapping<Mail::To>(&BufferBuilder::add_to); | ||
88 | propertyMapper.addMapping<Mail::Cc>(&BufferBuilder::add_cc); | ||
89 | propertyMapper.addMapping<Mail::Bcc>(&BufferBuilder::add_bcc); | ||
90 | propertyMapper.addMapping<Mail::Subject>(&BufferBuilder::add_subject); | ||
91 | propertyMapper.addMapping<Mail::Date>(&BufferBuilder::add_date); | ||
92 | propertyMapper.addMapping<Mail::Unread>(&BufferBuilder::add_unread); | ||
93 | propertyMapper.addMapping<Mail::Important>(&BufferBuilder::add_important); | ||
94 | propertyMapper.addMapping<Mail::Folder>(&BufferBuilder::add_folder); | ||
95 | propertyMapper.addMapping<Mail::MimeMessage>(&BufferBuilder::add_mimeMessage); | ||
96 | propertyMapper.addMapping<Mail::FullPayloadAvailable>(&BufferBuilder::add_fullPayloadAvailable); | ||
97 | propertyMapper.addMapping<Mail::Draft>(&BufferBuilder::add_draft); | ||
98 | propertyMapper.addMapping<Mail::Trash>(&BufferBuilder::add_trash); | ||
99 | propertyMapper.addMapping<Mail::Sent>(&BufferBuilder::add_sent); | ||
100 | propertyMapper.addMapping<Mail::MessageId>(&BufferBuilder::add_messageId); | ||
101 | propertyMapper.addMapping<Mail::ParentMessageId>(&BufferBuilder::add_parentMessageId); | ||
102 | } | 109 | } |
103 | 110 | ||
104 | 111 | ||
105 | void TypeImplementation<Folder>::configure(TypeIndex &index) | 112 | void TypeImplementation<Folder>::configure(TypeIndex &index) |
106 | { | 113 | { |
107 | index.addProperty<QByteArray>(Folder::Parent::name); | 114 | FolderIndexConfig::configure(index); |
108 | index.addProperty<QString>(Folder::Name::name); | ||
109 | } | 115 | } |
110 | 116 | ||
111 | void TypeImplementation<Folder>::configure(ReadPropertyMapper<Buffer> &propertyMapper) | 117 | QMap<QByteArray, int> TypeImplementation<Folder>::typeDatabases() |
112 | { | 118 | { |
113 | propertyMapper.addMapping<Folder::Parent, Buffer>(&Buffer::parent); | 119 | return merge(QMap<QByteArray, int>{{QByteArray{Folder::name} + ".main", 0}}, FolderIndexConfig::databases()); |
114 | propertyMapper.addMapping<Folder::Name, Buffer>(&Buffer::name); | ||
115 | propertyMapper.addMapping<Folder::Icon, Buffer>(&Buffer::icon); | ||
116 | propertyMapper.addMapping<Folder::SpecialPurpose, Buffer>(&Buffer::specialpurpose); | ||
117 | propertyMapper.addMapping<Folder::Enabled, Buffer>(&Buffer::enabled); | ||
118 | } | 120 | } |
119 | 121 | ||
120 | void TypeImplementation<Folder>::configure(WritePropertyMapper<BufferBuilder> &propertyMapper) | 122 | void TypeImplementation<Folder>::configure(PropertyMapper &propertyMapper) |
121 | { | 123 | { |
122 | propertyMapper.addMapping<Folder::Parent>(&BufferBuilder::add_parent); | 124 | SINK_REGISTER_SERIALIZER(propertyMapper, Folder, Parent, parent); |
123 | propertyMapper.addMapping<Folder::Name>(&BufferBuilder::add_name); | 125 | SINK_REGISTER_SERIALIZER(propertyMapper, Folder, Name, name); |
124 | propertyMapper.addMapping<Folder::Icon>(&BufferBuilder::add_icon); | 126 | SINK_REGISTER_SERIALIZER(propertyMapper, Folder, Icon, icon); |
125 | propertyMapper.addMapping<Folder::SpecialPurpose>(&BufferBuilder::add_specialpurpose); | 127 | SINK_REGISTER_SERIALIZER(propertyMapper, Folder, SpecialPurpose, specialpurpose); |
126 | propertyMapper.addMapping<Folder::Enabled>(&BufferBuilder::add_enabled); | 128 | SINK_REGISTER_SERIALIZER(propertyMapper, Folder, Enabled, enabled); |
127 | } | 129 | } |
128 | 130 | ||
129 | void TypeImplementation<Folder>::configure(IndexPropertyMapper &) | 131 | void TypeImplementation<Folder>::configure(IndexPropertyMapper &) |
@@ -134,29 +136,24 @@ void TypeImplementation<Folder>::configure(IndexPropertyMapper &) | |||
134 | 136 | ||
135 | void TypeImplementation<Contact>::configure(TypeIndex &index) | 137 | void TypeImplementation<Contact>::configure(TypeIndex &index) |
136 | { | 138 | { |
137 | index.addProperty<QByteArray>(Contact::Uid::name); | 139 | ContactIndexConfig::configure(index); |
138 | } | 140 | } |
139 | 141 | ||
140 | void TypeImplementation<Contact>::configure(ReadPropertyMapper<Buffer> &propertyMapper) | 142 | QMap<QByteArray, int> TypeImplementation<Contact>::typeDatabases() |
141 | { | 143 | { |
142 | propertyMapper.addMapping<Contact::Uid, Buffer>(&Buffer::uid); | 144 | return merge(QMap<QByteArray, int>{{QByteArray{Contact::name} + ".main", 0}}, ContactIndexConfig::databases()); |
143 | propertyMapper.addMapping<Contact::Fn, Buffer>(&Buffer::fn); | ||
144 | propertyMapper.addMapping<Contact::Emails, Buffer>(&Buffer::emails); | ||
145 | propertyMapper.addMapping<Contact::Vcard, Buffer>(&Buffer::vcard); | ||
146 | propertyMapper.addMapping<Contact::Addressbook, Buffer>(&Buffer::addressbook); | ||
147 | propertyMapper.addMapping<Contact::Firstname, Buffer>(&Buffer::firstname); | ||
148 | propertyMapper.addMapping<Contact::Lastname, Buffer>(&Buffer::lastname); | ||
149 | } | 145 | } |
150 | 146 | ||
151 | void TypeImplementation<Contact>::configure(WritePropertyMapper<BufferBuilder> &propertyMapper) | 147 | void TypeImplementation<Contact>::configure(PropertyMapper &propertyMapper) |
152 | { | 148 | { |
153 | propertyMapper.addMapping<Contact::Uid>(&BufferBuilder::add_uid); | 149 | SINK_REGISTER_SERIALIZER(propertyMapper, Contact, Uid, uid); |
154 | propertyMapper.addMapping<Contact::Fn>(&BufferBuilder::add_fn); | 150 | SINK_REGISTER_SERIALIZER(propertyMapper, Contact, Fn, fn); |
155 | propertyMapper.addMapping<Contact::Emails>(&BufferBuilder::add_emails); | 151 | SINK_REGISTER_SERIALIZER(propertyMapper, Contact, Emails, emails); |
156 | propertyMapper.addMapping<Contact::Vcard>(&BufferBuilder::add_vcard); | 152 | SINK_REGISTER_SERIALIZER(propertyMapper, Contact, Vcard, vcard); |
157 | propertyMapper.addMapping<Contact::Addressbook>(&BufferBuilder::add_addressbook); | 153 | SINK_REGISTER_SERIALIZER(propertyMapper, Contact, Addressbook, addressbook); |
158 | propertyMapper.addMapping<Contact::Firstname>(&BufferBuilder::add_firstname); | 154 | SINK_REGISTER_SERIALIZER(propertyMapper, Contact, Firstname, firstname); |
159 | propertyMapper.addMapping<Contact::Lastname>(&BufferBuilder::add_lastname); | 155 | SINK_REGISTER_SERIALIZER(propertyMapper, Contact, Lastname, lastname); |
156 | SINK_REGISTER_SERIALIZER(propertyMapper, Contact, Photo, photo); | ||
160 | } | 157 | } |
161 | 158 | ||
162 | void TypeImplementation<Contact>::configure(IndexPropertyMapper &) | 159 | void TypeImplementation<Contact>::configure(IndexPropertyMapper &) |
@@ -167,20 +164,18 @@ void TypeImplementation<Contact>::configure(IndexPropertyMapper &) | |||
167 | 164 | ||
168 | void TypeImplementation<Addressbook>::configure(TypeIndex &index) | 165 | void TypeImplementation<Addressbook>::configure(TypeIndex &index) |
169 | { | 166 | { |
170 | index.addProperty<QByteArray>(Addressbook::Parent::name); | 167 | AddressbookIndexConfig::configure(index); |
171 | index.addProperty<QString>(Addressbook::Name::name); | ||
172 | } | 168 | } |
173 | 169 | ||
174 | void TypeImplementation<Addressbook>::configure(ReadPropertyMapper<Buffer> &propertyMapper) | 170 | QMap<QByteArray, int> TypeImplementation<Addressbook>::typeDatabases() |
175 | { | 171 | { |
176 | propertyMapper.addMapping<Addressbook::Parent, Buffer>(&Buffer::parent); | 172 | return merge(QMap<QByteArray, int>{{QByteArray{Addressbook::name} + ".main", 0}}, AddressbookIndexConfig::databases()); |
177 | propertyMapper.addMapping<Addressbook::Name, Buffer>(&Buffer::name); | ||
178 | } | 173 | } |
179 | 174 | ||
180 | void TypeImplementation<Addressbook>::configure(WritePropertyMapper<BufferBuilder> &propertyMapper) | 175 | void TypeImplementation<Addressbook>::configure(PropertyMapper &propertyMapper) |
181 | { | 176 | { |
182 | propertyMapper.addMapping<Addressbook::Parent>(&BufferBuilder::add_parent); | 177 | SINK_REGISTER_SERIALIZER(propertyMapper, Addressbook, Parent, parent); |
183 | propertyMapper.addMapping<Addressbook::Name>(&BufferBuilder::add_name); | 178 | SINK_REGISTER_SERIALIZER(propertyMapper, Addressbook, Name, name); |
184 | } | 179 | } |
185 | 180 | ||
186 | void TypeImplementation<Addressbook>::configure(IndexPropertyMapper &) | 181 | void TypeImplementation<Addressbook>::configure(IndexPropertyMapper &) |
@@ -191,26 +186,24 @@ void TypeImplementation<Addressbook>::configure(IndexPropertyMapper &) | |||
191 | 186 | ||
192 | void TypeImplementation<Event>::configure(TypeIndex &index) | 187 | void TypeImplementation<Event>::configure(TypeIndex &index) |
193 | { | 188 | { |
194 | index.addProperty<QByteArray>(Event::Uid::name); | 189 | EventIndexConfig::configure(index); |
195 | } | 190 | } |
196 | 191 | ||
197 | void TypeImplementation<Event>::configure(ReadPropertyMapper<Buffer> &propertyMapper) | 192 | QMap<QByteArray, int> TypeImplementation<Event>::typeDatabases() |
198 | { | 193 | { |
199 | propertyMapper.addMapping<Event::Summary, Buffer>(&Buffer::summary); | 194 | return merge(QMap<QByteArray, int>{{QByteArray{Event::name} + ".main", 0}}, EventIndexConfig::databases()); |
200 | propertyMapper.addMapping<Event::Description, Buffer>(&Buffer::description); | ||
201 | propertyMapper.addMapping<Event::Uid, Buffer>(&Buffer::uid); | ||
202 | propertyMapper.addMapping<Event::Attachment, Buffer>(&Buffer::attachment); | ||
203 | } | 195 | } |
204 | 196 | ||
205 | void TypeImplementation<Event>::configure(WritePropertyMapper<BufferBuilder> &propertyMapper) | 197 | void TypeImplementation<Event>::configure(PropertyMapper &propertyMapper) |
206 | { | 198 | { |
207 | propertyMapper.addMapping<Event::Summary>(&BufferBuilder::add_summary); | 199 | SINK_REGISTER_SERIALIZER(propertyMapper, Event, Summary, summary); |
208 | propertyMapper.addMapping<Event::Description>(&BufferBuilder::add_description); | 200 | SINK_REGISTER_SERIALIZER(propertyMapper, Event, Description, description); |
209 | propertyMapper.addMapping<Event::Uid>(&BufferBuilder::add_uid); | 201 | SINK_REGISTER_SERIALIZER(propertyMapper, Event, Uid, uid); |
210 | propertyMapper.addMapping<Event::Attachment>(&BufferBuilder::add_attachment); | 202 | SINK_REGISTER_SERIALIZER(propertyMapper, Event, Attachment, attachment); |
211 | } | 203 | } |
212 | 204 | ||
213 | void TypeImplementation<Event>::configure(IndexPropertyMapper &) | 205 | void TypeImplementation<Event>::configure(IndexPropertyMapper &) |
214 | { | 206 | { |
215 | 207 | ||
216 | } | 208 | } |
209 | |||
diff --git a/common/domain/typeimplementations.h b/common/domain/typeimplementations.h index 37d6ca9..d36dfc1 100644 --- a/common/domain/typeimplementations.h +++ b/common/domain/typeimplementations.h | |||
@@ -26,10 +26,7 @@ | |||
26 | #include "contact_generated.h" | 26 | #include "contact_generated.h" |
27 | #include "addressbook_generated.h" | 27 | #include "addressbook_generated.h" |
28 | 28 | ||
29 | template<typename T> | 29 | class PropertyMapper; |
30 | class ReadPropertyMapper; | ||
31 | template<typename T> | ||
32 | class WritePropertyMapper; | ||
33 | class IndexPropertyMapper; | 30 | class IndexPropertyMapper; |
34 | 31 | ||
35 | class TypeIndex; | 32 | class TypeIndex; |
@@ -48,9 +45,9 @@ public: | |||
48 | typedef Sink::ApplicationDomain::Buffer::Mail Buffer; | 45 | typedef Sink::ApplicationDomain::Buffer::Mail Buffer; |
49 | typedef Sink::ApplicationDomain::Buffer::MailBuilder BufferBuilder; | 46 | typedef Sink::ApplicationDomain::Buffer::MailBuilder BufferBuilder; |
50 | static void configure(TypeIndex &index); | 47 | static void configure(TypeIndex &index); |
51 | static void configure(ReadPropertyMapper<Buffer> &propertyMapper); | 48 | static void configure(PropertyMapper &propertyMapper); |
52 | static void configure(WritePropertyMapper<BufferBuilder> &propertyMapper); | ||
53 | static void configure(IndexPropertyMapper &indexPropertyMapper); | 49 | static void configure(IndexPropertyMapper &indexPropertyMapper); |
50 | static QMap<QByteArray, int> typeDatabases(); | ||
54 | }; | 51 | }; |
55 | 52 | ||
56 | template<> | 53 | template<> |
@@ -59,9 +56,9 @@ public: | |||
59 | typedef Sink::ApplicationDomain::Buffer::Folder Buffer; | 56 | typedef Sink::ApplicationDomain::Buffer::Folder Buffer; |
60 | typedef Sink::ApplicationDomain::Buffer::FolderBuilder BufferBuilder; | 57 | typedef Sink::ApplicationDomain::Buffer::FolderBuilder BufferBuilder; |
61 | static void configure(TypeIndex &); | 58 | static void configure(TypeIndex &); |
62 | static void configure(ReadPropertyMapper<Buffer> &); | 59 | static void configure(PropertyMapper &); |
63 | static void configure(WritePropertyMapper<BufferBuilder> &); | ||
64 | static void configure(IndexPropertyMapper &indexPropertyMapper); | 60 | static void configure(IndexPropertyMapper &indexPropertyMapper); |
61 | static QMap<QByteArray, int> typeDatabases(); | ||
65 | }; | 62 | }; |
66 | 63 | ||
67 | template<> | 64 | template<> |
@@ -70,9 +67,9 @@ public: | |||
70 | typedef Sink::ApplicationDomain::Buffer::Contact Buffer; | 67 | typedef Sink::ApplicationDomain::Buffer::Contact Buffer; |
71 | typedef Sink::ApplicationDomain::Buffer::ContactBuilder BufferBuilder; | 68 | typedef Sink::ApplicationDomain::Buffer::ContactBuilder BufferBuilder; |
72 | static void configure(TypeIndex &); | 69 | static void configure(TypeIndex &); |
73 | static void configure(ReadPropertyMapper<Buffer> &); | 70 | static void configure(PropertyMapper &); |
74 | static void configure(WritePropertyMapper<BufferBuilder> &); | ||
75 | static void configure(IndexPropertyMapper &indexPropertyMapper); | 71 | static void configure(IndexPropertyMapper &indexPropertyMapper); |
72 | static QMap<QByteArray, int> typeDatabases(); | ||
76 | }; | 73 | }; |
77 | 74 | ||
78 | template<> | 75 | template<> |
@@ -81,9 +78,9 @@ public: | |||
81 | typedef Sink::ApplicationDomain::Buffer::Addressbook Buffer; | 78 | typedef Sink::ApplicationDomain::Buffer::Addressbook Buffer; |
82 | typedef Sink::ApplicationDomain::Buffer::AddressbookBuilder BufferBuilder; | 79 | typedef Sink::ApplicationDomain::Buffer::AddressbookBuilder BufferBuilder; |
83 | static void configure(TypeIndex &); | 80 | static void configure(TypeIndex &); |
84 | static void configure(ReadPropertyMapper<Buffer> &); | 81 | static void configure(PropertyMapper &); |
85 | static void configure(WritePropertyMapper<BufferBuilder> &); | ||
86 | static void configure(IndexPropertyMapper &indexPropertyMapper); | 82 | static void configure(IndexPropertyMapper &indexPropertyMapper); |
83 | static QMap<QByteArray, int> typeDatabases(); | ||
87 | }; | 84 | }; |
88 | 85 | ||
89 | template<> | 86 | template<> |
@@ -92,9 +89,9 @@ public: | |||
92 | typedef Sink::ApplicationDomain::Buffer::Event Buffer; | 89 | typedef Sink::ApplicationDomain::Buffer::Event Buffer; |
93 | typedef Sink::ApplicationDomain::Buffer::EventBuilder BufferBuilder; | 90 | typedef Sink::ApplicationDomain::Buffer::EventBuilder BufferBuilder; |
94 | static void configure(TypeIndex &); | 91 | static void configure(TypeIndex &); |
95 | static void configure(ReadPropertyMapper<Buffer> &); | 92 | static void configure(PropertyMapper &); |
96 | static void configure(WritePropertyMapper<BufferBuilder> &); | ||
97 | static void configure(IndexPropertyMapper &indexPropertyMapper); | 93 | static void configure(IndexPropertyMapper &indexPropertyMapper); |
94 | static QMap<QByteArray, int> typeDatabases(); | ||
98 | }; | 95 | }; |
99 | 96 | ||
100 | } | 97 | } |
diff --git a/common/domain/typeimplementations_p.h b/common/domain/typeimplementations_p.h new file mode 100644 index 0000000..6f77a2d --- /dev/null +++ b/common/domain/typeimplementations_p.h | |||
@@ -0,0 +1,154 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2015 Christian Mollekopf <chrigi_1@fastmail.fm> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the | ||
16 | * Free Software Foundation, Inc., | ||
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
18 | */ | ||
19 | |||
20 | #include "typeindex.h" | ||
21 | #include <QMap> | ||
22 | |||
23 | template <typename T, typename First> | ||
24 | void mergeImpl(T &map, First f) | ||
25 | { | ||
26 | for (auto it = f.constBegin(); it != f.constEnd(); it++) { | ||
27 | map.insert(it.key(), it.value()); | ||
28 | } | ||
29 | } | ||
30 | |||
31 | template <typename T, typename First, typename ... Tail> | ||
32 | void mergeImpl(T &map, First f, Tail ...maps) | ||
33 | { | ||
34 | for (auto it = f.constBegin(); it != f.constEnd(); it++) { | ||
35 | map.insert(it.key(), it.value()); | ||
36 | } | ||
37 | mergeImpl<T, Tail...>(map, maps...); | ||
38 | } | ||
39 | |||
40 | template <typename First, typename ... Tail> | ||
41 | First merge(First f, Tail ...maps) | ||
42 | { | ||
43 | First map; | ||
44 | mergeImpl(map, f, maps...); | ||
45 | return map; | ||
46 | } | ||
47 | |||
48 | template <typename Property> | ||
49 | class ValueIndex | ||
50 | { | ||
51 | public: | ||
52 | static void configure(TypeIndex &index) | ||
53 | { | ||
54 | index.addProperty<Property>(); | ||
55 | } | ||
56 | |||
57 | template <typename EntityType> | ||
58 | static QMap<QByteArray, int> databases() | ||
59 | { | ||
60 | return {{QByteArray{EntityType::name} +".index." + Property::name, 1}}; | ||
61 | } | ||
62 | }; | ||
63 | |||
64 | |||
65 | template <typename Property, typename SortProperty> | ||
66 | class SortedIndex | ||
67 | { | ||
68 | public: | ||
69 | static void configure(TypeIndex &index) | ||
70 | { | ||
71 | index.addPropertyWithSorting<Property, SortProperty>(); | ||
72 | } | ||
73 | |||
74 | template <typename EntityType> | ||
75 | static QMap<QByteArray, int> databases() | ||
76 | { | ||
77 | return {{QByteArray{EntityType::name} +".index." + Property::name + ".sort." + SortProperty::name, 1}}; | ||
78 | } | ||
79 | }; | ||
80 | |||
81 | template <typename Property, typename SecondaryProperty> | ||
82 | class SecondaryIndex | ||
83 | { | ||
84 | public: | ||
85 | static void configure(TypeIndex &index) | ||
86 | { | ||
87 | index.addSecondaryProperty<Property, SecondaryProperty>(); | ||
88 | } | ||
89 | |||
90 | template <typename EntityType> | ||
91 | static QMap<QByteArray, int> databases() | ||
92 | { | ||
93 | return {{QByteArray{EntityType::name} +".index." + Property::name + SecondaryProperty::name, 1}}; | ||
94 | } | ||
95 | }; | ||
96 | |||
97 | template <typename Property, typename SecondaryProperty, typename Indexer> | ||
98 | class CustomSecondaryIndex | ||
99 | { | ||
100 | public: | ||
101 | static void configure(TypeIndex &index) | ||
102 | { | ||
103 | index.addSecondaryPropertyIndexer<Property, SecondaryProperty, Indexer>(); | ||
104 | } | ||
105 | |||
106 | template <typename EntityType> | ||
107 | static QMap<QByteArray, int> databases() | ||
108 | { | ||
109 | return Indexer::databases(); | ||
110 | } | ||
111 | }; | ||
112 | |||
113 | template <typename EntityType, typename ... Indexes> | ||
114 | class IndexConfig | ||
115 | { | ||
116 | template <typename T> | ||
117 | static void applyIndex(TypeIndex &index) | ||
118 | { | ||
119 | T::configure(index); | ||
120 | } | ||
121 | |||
122 | ///Apply recursively for parameter pack | ||
123 | template <typename First, typename Second, typename ... Tail> | ||
124 | static void applyIndex(TypeIndex &index) | ||
125 | { | ||
126 | applyIndex<First>(index); | ||
127 | applyIndex<Second, Tail...>(index); | ||
128 | } | ||
129 | |||
130 | template <typename T> | ||
131 | static QMap<QByteArray, int> getDbs() | ||
132 | { | ||
133 | return T::template databases<EntityType>(); | ||
134 | } | ||
135 | |||
136 | template <typename First, typename Second, typename ... Tail> | ||
137 | static QMap<QByteArray, int> getDbs() | ||
138 | { | ||
139 | return merge(getDbs<First>(), getDbs<Second, Tail...>()); | ||
140 | } | ||
141 | |||
142 | public: | ||
143 | static void configure(TypeIndex &index) | ||
144 | { | ||
145 | applyIndex<Indexes...>(index); | ||
146 | } | ||
147 | |||
148 | static QMap<QByteArray, int> databases() | ||
149 | { | ||
150 | return getDbs<Indexes...>(); | ||
151 | } | ||
152 | |||
153 | }; | ||
154 | |||
diff --git a/common/domainadaptor.h b/common/domainadaptor.h index af5d5fc..f981a1f 100644 --- a/common/domainadaptor.h +++ b/common/domainadaptor.h | |||
@@ -29,21 +29,19 @@ | |||
29 | #include "domain/typeimplementations.h" | 29 | #include "domain/typeimplementations.h" |
30 | #include "bufferadaptor.h" | 30 | #include "bufferadaptor.h" |
31 | #include "entity_generated.h" | 31 | #include "entity_generated.h" |
32 | #include "metadata_generated.h" | ||
33 | #include "entitybuffer.h" | 32 | #include "entitybuffer.h" |
34 | #include "propertymapper.h" | 33 | #include "propertymapper.h" |
35 | #include "log.h" | 34 | #include "log.h" |
36 | #include "dummy_generated.h" | ||
37 | 35 | ||
38 | /** | 36 | /** |
39 | * Create a buffer from a domain object using the provided mappings | 37 | * Create a buffer from a domain object using the provided mappings |
40 | */ | 38 | */ |
41 | template <class Builder, class Buffer> | 39 | template <class Builder, class Buffer> |
42 | flatbuffers::Offset<Buffer> | 40 | flatbuffers::Offset<Buffer> |
43 | createBufferPart(const Sink::ApplicationDomain::ApplicationDomainType &domainObject, flatbuffers::FlatBufferBuilder &fbb, const WritePropertyMapper<Builder> &mapper) | 41 | createBufferPart(const Sink::ApplicationDomain::ApplicationDomainType &domainObject, flatbuffers::FlatBufferBuilder &fbb, const PropertyMapper &mapper) |
44 | { | 42 | { |
45 | // First create a primitives such as strings using the mappings | 43 | // First create a primitives such as strings using the mappings |
46 | QList<std::function<void(Builder &)>> propertiesToAddToResource; | 44 | QList<std::function<void(void *builder)>> propertiesToAddToResource; |
47 | for (const auto &property : domainObject.changedProperties()) { | 45 | for (const auto &property : domainObject.changedProperties()) { |
48 | // SinkTrace() << "copying property " << property; | 46 | // SinkTrace() << "copying property " << property; |
49 | const auto value = domainObject.getProperty(property); | 47 | const auto value = domainObject.getProperty(property); |
@@ -57,7 +55,7 @@ createBufferPart(const Sink::ApplicationDomain::ApplicationDomainType &domainObj | |||
57 | // Then create all porperties using the above generated builderCalls | 55 | // Then create all porperties using the above generated builderCalls |
58 | Builder builder(fbb); | 56 | Builder builder(fbb); |
59 | for (auto propertyBuilder : propertiesToAddToResource) { | 57 | for (auto propertyBuilder : propertiesToAddToResource) { |
60 | propertyBuilder(builder); | 58 | propertyBuilder(&builder); |
61 | } | 59 | } |
62 | return builder.Finish(); | 60 | return builder.Finish(); |
63 | } | 61 | } |
@@ -68,7 +66,7 @@ createBufferPart(const Sink::ApplicationDomain::ApplicationDomainType &domainObj | |||
68 | * After this the buffer can be extracted from the FlatBufferBuilder object. | 66 | * After this the buffer can be extracted from the FlatBufferBuilder object. |
69 | */ | 67 | */ |
70 | template <typename Buffer, typename BufferBuilder> | 68 | template <typename Buffer, typename BufferBuilder> |
71 | static void createBufferPartBuffer(const Sink::ApplicationDomain::ApplicationDomainType &domainObject, flatbuffers::FlatBufferBuilder &fbb, WritePropertyMapper<BufferBuilder> &mapper) | 69 | static void createBufferPartBuffer(const Sink::ApplicationDomain::ApplicationDomainType &domainObject, flatbuffers::FlatBufferBuilder &fbb, PropertyMapper &mapper) |
72 | { | 70 | { |
73 | auto pos = createBufferPart<BufferBuilder, Buffer>(domainObject, fbb, mapper); | 71 | auto pos = createBufferPart<BufferBuilder, Buffer>(domainObject, fbb, mapper); |
74 | // Because we cannot template the following call | 72 | // Because we cannot template the following call |
@@ -120,10 +118,8 @@ private: | |||
120 | /** | 118 | /** |
121 | * A generic adaptor implementation that uses a property mapper to read/write values. | 119 | * A generic adaptor implementation that uses a property mapper to read/write values. |
122 | */ | 120 | */ |
123 | template <class LocalBuffer, class ResourceBuffer> | ||
124 | class DatastoreBufferAdaptor : public Sink::ApplicationDomain::BufferAdaptor | 121 | class DatastoreBufferAdaptor : public Sink::ApplicationDomain::BufferAdaptor |
125 | { | 122 | { |
126 | SINK_DEBUG_AREA("bufferadaptor") | ||
127 | public: | 123 | public: |
128 | DatastoreBufferAdaptor() : BufferAdaptor() | 124 | DatastoreBufferAdaptor() : BufferAdaptor() |
129 | { | 125 | { |
@@ -137,9 +133,7 @@ public: | |||
137 | 133 | ||
138 | virtual QVariant getProperty(const QByteArray &key) const Q_DECL_OVERRIDE | 134 | virtual QVariant getProperty(const QByteArray &key) const Q_DECL_OVERRIDE |
139 | { | 135 | { |
140 | if (mResourceBuffer && mResourceMapper->hasMapping(key)) { | 136 | if (mLocalBuffer && mLocalMapper->hasMapping(key)) { |
141 | return mResourceMapper->getProperty(key, mResourceBuffer); | ||
142 | } else if (mLocalBuffer && mLocalMapper->hasMapping(key)) { | ||
143 | return mLocalMapper->getProperty(key, mLocalBuffer); | 137 | return mLocalMapper->getProperty(key, mLocalBuffer); |
144 | } else if (mIndex && mIndexMapper->hasMapping(key)) { | 138 | } else if (mIndex && mIndexMapper->hasMapping(key)) { |
145 | return mIndexMapper->getProperty(key, *mIndex, *this); | 139 | return mIndexMapper->getProperty(key, *mIndex, *this); |
@@ -152,23 +146,21 @@ public: | |||
152 | */ | 146 | */ |
153 | virtual QList<QByteArray> availableProperties() const Q_DECL_OVERRIDE | 147 | virtual QList<QByteArray> availableProperties() const Q_DECL_OVERRIDE |
154 | { | 148 | { |
155 | return mResourceMapper->availableProperties() + mLocalMapper->availableProperties() + mIndexMapper->availableProperties(); | 149 | return mLocalMapper->availableProperties() + mIndexMapper->availableProperties(); |
156 | } | 150 | } |
157 | 151 | ||
158 | LocalBuffer const *mLocalBuffer; | 152 | void const *mLocalBuffer; |
159 | ResourceBuffer const *mResourceBuffer; | 153 | QSharedPointer<PropertyMapper> mLocalMapper; |
160 | QSharedPointer<ReadPropertyMapper<LocalBuffer>> mLocalMapper; | ||
161 | QSharedPointer<ReadPropertyMapper<ResourceBuffer>> mResourceMapper; | ||
162 | QSharedPointer<IndexPropertyMapper> mIndexMapper; | 154 | QSharedPointer<IndexPropertyMapper> mIndexMapper; |
163 | TypeIndex *mIndex; | 155 | TypeIndex *mIndex; |
164 | }; | 156 | }; |
165 | 157 | ||
166 | /** | 158 | /** |
167 | * The factory should define how to go from an entitybuffer (local + resource buffer), to a domain type adapter. | 159 | * The factory should define how to go from an entitybuffer (local buffer), to a domain type adapter. |
168 | * It defines how values are split accross local and resource buffer. | 160 | * It defines how values are split accross local and resource buffer. |
169 | * This is required by the facade the read the value, and by the pipeline preprocessors to access the domain values in a generic way. | 161 | * This is required by the facade the read the value, and by the pipeline preprocessors to access the domain values in a generic way. |
170 | */ | 162 | */ |
171 | template <typename DomainType, typename ResourceBuffer = Sink::ApplicationDomain::Buffer::Dummy, typename ResourceBuilder = Sink::ApplicationDomain::Buffer::DummyBuilder> | 163 | template <typename DomainType> |
172 | class SINK_EXPORT DomainTypeAdaptorFactory : public DomainTypeAdaptorFactoryInterface | 164 | class SINK_EXPORT DomainTypeAdaptorFactory : public DomainTypeAdaptorFactoryInterface |
173 | { | 165 | { |
174 | typedef typename Sink::ApplicationDomain::TypeImplementation<DomainType>::Buffer LocalBuffer; | 166 | typedef typename Sink::ApplicationDomain::TypeImplementation<DomainType>::Buffer LocalBuffer; |
@@ -176,31 +168,25 @@ class SINK_EXPORT DomainTypeAdaptorFactory : public DomainTypeAdaptorFactoryInte | |||
176 | 168 | ||
177 | public: | 169 | public: |
178 | DomainTypeAdaptorFactory() | 170 | DomainTypeAdaptorFactory() |
179 | : mLocalMapper(QSharedPointer<ReadPropertyMapper<LocalBuffer>>::create()), | 171 | : mPropertyMapper(QSharedPointer<PropertyMapper>::create()), |
180 | mResourceMapper(QSharedPointer<ReadPropertyMapper<ResourceBuffer>>::create()), | ||
181 | mLocalWriteMapper(QSharedPointer<WritePropertyMapper<LocalBuilder>>::create()), | ||
182 | mResourceWriteMapper(QSharedPointer<WritePropertyMapper<ResourceBuilder>>::create()), | ||
183 | mIndexMapper(QSharedPointer<IndexPropertyMapper>::create()) | 172 | mIndexMapper(QSharedPointer<IndexPropertyMapper>::create()) |
184 | { | 173 | { |
185 | Sink::ApplicationDomain::TypeImplementation<DomainType>::configure(*mLocalMapper); | 174 | Sink::ApplicationDomain::TypeImplementation<DomainType>::configure(*mPropertyMapper); |
186 | Sink::ApplicationDomain::TypeImplementation<DomainType>::configure(*mLocalWriteMapper); | ||
187 | Sink::ApplicationDomain::TypeImplementation<DomainType>::configure(*mIndexMapper); | 175 | Sink::ApplicationDomain::TypeImplementation<DomainType>::configure(*mIndexMapper); |
188 | } | 176 | } |
189 | 177 | ||
190 | virtual ~DomainTypeAdaptorFactory(){}; | 178 | virtual ~DomainTypeAdaptorFactory(){}; |
191 | 179 | ||
192 | /** | 180 | /** |
193 | * Creates an adaptor for the given domain and resource types. | 181 | * Creates an adaptor for the given domain types. |
194 | * | 182 | * |
195 | * This returns by default a DatastoreBufferAdaptor initialized with the corresponding property mappers. | 183 | * This returns by default a DatastoreBufferAdaptor initialized with the corresponding property mappers. |
196 | */ | 184 | */ |
197 | virtual QSharedPointer<Sink::ApplicationDomain::BufferAdaptor> createAdaptor(const Sink::Entity &entity, TypeIndex *index = nullptr) Q_DECL_OVERRIDE | 185 | virtual QSharedPointer<Sink::ApplicationDomain::BufferAdaptor> createAdaptor(const Sink::Entity &entity, TypeIndex *index = nullptr) Q_DECL_OVERRIDE |
198 | { | 186 | { |
199 | auto adaptor = QSharedPointer<DatastoreBufferAdaptor<LocalBuffer, ResourceBuffer>>::create(); | 187 | auto adaptor = QSharedPointer<DatastoreBufferAdaptor>::create(); |
200 | adaptor->mLocalBuffer = Sink::EntityBuffer::readBuffer<LocalBuffer>(entity.local()); | 188 | adaptor->mLocalBuffer = Sink::EntityBuffer::readBuffer<LocalBuffer>(entity.local()); |
201 | adaptor->mLocalMapper = mLocalMapper; | 189 | adaptor->mLocalMapper = mPropertyMapper; |
202 | adaptor->mResourceBuffer = Sink::EntityBuffer::readBuffer<ResourceBuffer>(entity.resource()); | ||
203 | adaptor->mResourceMapper = mResourceMapper; | ||
204 | adaptor->mIndexMapper = mIndexMapper; | 190 | adaptor->mIndexMapper = mIndexMapper; |
205 | adaptor->mIndex = index; | 191 | adaptor->mIndex = index; |
206 | return adaptor; | 192 | return adaptor; |
@@ -210,18 +196,8 @@ public: | |||
210 | createBuffer(const Sink::ApplicationDomain::ApplicationDomainType &domainObject, flatbuffers::FlatBufferBuilder &fbb, void const *metadataData = 0, size_t metadataSize = 0) Q_DECL_OVERRIDE | 196 | createBuffer(const Sink::ApplicationDomain::ApplicationDomainType &domainObject, flatbuffers::FlatBufferBuilder &fbb, void const *metadataData = 0, size_t metadataSize = 0) Q_DECL_OVERRIDE |
211 | { | 197 | { |
212 | flatbuffers::FlatBufferBuilder localFbb; | 198 | flatbuffers::FlatBufferBuilder localFbb; |
213 | if (mLocalWriteMapper) { | 199 | createBufferPartBuffer<LocalBuffer, LocalBuilder>(domainObject, localFbb, *mPropertyMapper); |
214 | // SinkTrace() << "Creating local buffer part"; | 200 | Sink::EntityBuffer::assembleEntityBuffer(fbb, metadataData, metadataSize, 0, 0, localFbb.GetBufferPointer(), localFbb.GetSize()); |
215 | createBufferPartBuffer<LocalBuffer, LocalBuilder>(domainObject, localFbb, *mLocalWriteMapper); | ||
216 | } | ||
217 | |||
218 | flatbuffers::FlatBufferBuilder resFbb; | ||
219 | if (mResourceWriteMapper) { | ||
220 | // SinkTrace() << "Creating resouce buffer part"; | ||
221 | createBufferPartBuffer<ResourceBuffer, ResourceBuilder>(domainObject, resFbb, *mResourceWriteMapper); | ||
222 | } | ||
223 | |||
224 | Sink::EntityBuffer::assembleEntityBuffer(fbb, metadataData, metadataSize, resFbb.GetBufferPointer(), resFbb.GetSize(), localFbb.GetBufferPointer(), localFbb.GetSize()); | ||
225 | return true; | 201 | return true; |
226 | } | 202 | } |
227 | 203 | ||
@@ -236,10 +212,7 @@ public: | |||
236 | 212 | ||
237 | 213 | ||
238 | protected: | 214 | protected: |
239 | QSharedPointer<ReadPropertyMapper<LocalBuffer>> mLocalMapper; | 215 | QSharedPointer<PropertyMapper> mPropertyMapper; |
240 | QSharedPointer<ReadPropertyMapper<ResourceBuffer>> mResourceMapper; | ||
241 | QSharedPointer<WritePropertyMapper<LocalBuilder>> mLocalWriteMapper; | ||
242 | QSharedPointer<WritePropertyMapper<ResourceBuilder>> mResourceWriteMapper; | ||
243 | QSharedPointer<IndexPropertyMapper> mIndexMapper; | 216 | QSharedPointer<IndexPropertyMapper> mIndexMapper; |
244 | }; | 217 | }; |
245 | 218 | ||
diff --git a/common/facade.cpp b/common/facade.cpp index 38ec1dc..9c14a23 100644 --- a/common/facade.cpp +++ b/common/facade.cpp | |||
@@ -104,6 +104,7 @@ KAsync::Job<void> GenericFacade<DomainType>::remove(const DomainType &domainObje | |||
104 | template <class DomainType> | 104 | template <class DomainType> |
105 | QPair<KAsync::Job<void>, typename ResultEmitter<typename DomainType::Ptr>::Ptr> GenericFacade<DomainType>::load(const Sink::Query &query, const Log::Context &ctx) | 105 | QPair<KAsync::Job<void>, typename ResultEmitter<typename DomainType::Ptr>::Ptr> GenericFacade<DomainType>::load(const Sink::Query &query, const Log::Context &ctx) |
106 | { | 106 | { |
107 | Q_ASSERT(DomainType::name == query.type() || query.type().isEmpty()); | ||
107 | // The runner lives for the lifetime of the query | 108 | // The runner lives for the lifetime of the query |
108 | auto runner = new QueryRunner<DomainType>(query, mResourceContext, bufferTypeForDomainType(), ctx); | 109 | auto runner = new QueryRunner<DomainType>(query, mResourceContext, bufferTypeForDomainType(), ctx); |
109 | runner->setResultTransformation(mResultTransformation); | 110 | runner->setResultTransformation(mResultTransformation); |
diff --git a/common/facade.h b/common/facade.h index 45c718b..0ec2e59 100644 --- a/common/facade.h +++ b/common/facade.h | |||
@@ -48,7 +48,6 @@ template <typename DomainType> | |||
48 | class SINK_EXPORT GenericFacade : public Sink::StoreFacade<DomainType> | 48 | class SINK_EXPORT GenericFacade : public Sink::StoreFacade<DomainType> |
49 | { | 49 | { |
50 | protected: | 50 | protected: |
51 | SINK_DEBUG_AREA("facade") | ||
52 | SINK_DEBUG_COMPONENT(mResourceContext.resourceInstanceIdentifier) | 51 | SINK_DEBUG_COMPONENT(mResourceContext.resourceInstanceIdentifier) |
53 | public: | 52 | public: |
54 | /** | 53 | /** |
diff --git a/common/genericresource.h b/common/genericresource.h index 558145c..bffc697 100644 --- a/common/genericresource.h +++ b/common/genericresource.h | |||
@@ -35,8 +35,6 @@ class CommandProcessor; | |||
35 | */ | 35 | */ |
36 | class SINK_EXPORT GenericResource : public Resource | 36 | class SINK_EXPORT GenericResource : public Resource |
37 | { | 37 | { |
38 | protected: | ||
39 | SINK_DEBUG_AREA("resource") | ||
40 | public: | 38 | public: |
41 | GenericResource(const Sink::ResourceContext &context, const QSharedPointer<Pipeline> &pipeline = QSharedPointer<Pipeline>()); | 39 | GenericResource(const Sink::ResourceContext &context, const QSharedPointer<Pipeline> &pipeline = QSharedPointer<Pipeline>()); |
42 | virtual ~GenericResource(); | 40 | virtual ~GenericResource(); |
diff --git a/common/listener.cpp b/common/listener.cpp index 983e438..c9fd9d3 100644 --- a/common/listener.cpp +++ b/common/listener.cpp | |||
@@ -405,6 +405,7 @@ void Listener::updateClientsWithRevision(qint64 revision) | |||
405 | 405 | ||
406 | SinkTrace() << "Sending revision update for " << client.name << revision; | 406 | SinkTrace() << "Sending revision update for " << client.name << revision; |
407 | Sink::Commands::write(client.socket, ++m_messageId, Sink::Commands::RevisionUpdateCommand, m_fbb); | 407 | Sink::Commands::write(client.socket, ++m_messageId, Sink::Commands::RevisionUpdateCommand, m_fbb); |
408 | client.socket->flush(); | ||
408 | } | 409 | } |
409 | m_fbb.Clear(); | 410 | m_fbb.Clear(); |
410 | } | 411 | } |
@@ -420,6 +421,8 @@ void Listener::notify(const Sink::Notification ¬ification) | |||
420 | builder.add_identifier(idString); | 421 | builder.add_identifier(idString); |
421 | builder.add_message(messageString); | 422 | builder.add_message(messageString); |
422 | builder.add_entities(entities); | 423 | builder.add_entities(entities); |
424 | builder.add_progress(notification.progress); | ||
425 | builder.add_total(notification.total); | ||
423 | auto command = builder.Finish(); | 426 | auto command = builder.Finish(); |
424 | Sink::Commands::FinishNotificationBuffer(m_fbb, command); | 427 | Sink::Commands::FinishNotificationBuffer(m_fbb, command); |
425 | for (Client &client : m_connections) { | 428 | for (Client &client : m_connections) { |
diff --git a/common/listener.h b/common/listener.h index 8d8b529..f29130d 100644 --- a/common/listener.h +++ b/common/listener.h | |||
@@ -55,8 +55,6 @@ public: | |||
55 | class SINK_EXPORT Listener : public QObject | 55 | class SINK_EXPORT Listener : public QObject |
56 | { | 56 | { |
57 | Q_OBJECT | 57 | Q_OBJECT |
58 | SINK_DEBUG_AREA("communication") | ||
59 | |||
60 | public: | 58 | public: |
61 | Listener(const QByteArray &resourceName, const QByteArray &resourceType, QObject *parent = 0); | 59 | Listener(const QByteArray &resourceName, const QByteArray &resourceType, QObject *parent = 0); |
62 | ~Listener(); | 60 | ~Listener(); |
diff --git a/common/log.cpp b/common/log.cpp index 045b3cc..5dfb872 100644 --- a/common/log.cpp +++ b/common/log.cpp | |||
@@ -1,6 +1,7 @@ | |||
1 | #include "log.h" | 1 | #include "log.h" |
2 | 2 | ||
3 | #include <QString> | 3 | #include <QString> |
4 | #include <QDir> | ||
4 | #include <QIODevice> | 5 | #include <QIODevice> |
5 | #include <QCoreApplication> | 6 | #include <QCoreApplication> |
6 | #include <QSettings> | 7 | #include <QSettings> |
@@ -12,12 +13,18 @@ | |||
12 | #include <memory> | 13 | #include <memory> |
13 | #include <atomic> | 14 | #include <atomic> |
14 | #include <definitions.h> | 15 | #include <definitions.h> |
16 | #include <QThreadStorage> | ||
17 | #include <QStringBuilder> | ||
15 | 18 | ||
16 | using namespace Sink::Log; | 19 | using namespace Sink::Log; |
17 | 20 | ||
18 | static QSharedPointer<QSettings> config() | 21 | static QThreadStorage<QSharedPointer<QSettings>> sSettings; |
22 | static QSettings &config() | ||
19 | { | 23 | { |
20 | return QSharedPointer<QSettings>::create(Sink::configLocation() + "/log.ini", QSettings::IniFormat); | 24 | if (!sSettings.hasLocalData()) { |
25 | sSettings.setLocalData(QSharedPointer<QSettings>::create(Sink::configLocation() + "/log.ini", QSettings::IniFormat)); | ||
26 | } | ||
27 | return *sSettings.localData(); | ||
21 | } | 28 | } |
22 | 29 | ||
23 | static QByteArray sPrimaryComponent; | 30 | static QByteArray sPrimaryComponent; |
@@ -173,22 +180,22 @@ DebugLevel Sink::Log::debugLevelFromName(const QByteArray &name) | |||
173 | 180 | ||
174 | void Sink::Log::setDebugOutputLevel(DebugLevel debugLevel) | 181 | void Sink::Log::setDebugOutputLevel(DebugLevel debugLevel) |
175 | { | 182 | { |
176 | config()->setValue("level", debugLevel); | 183 | config().setValue("level", debugLevel); |
177 | } | 184 | } |
178 | 185 | ||
179 | Sink::Log::DebugLevel Sink::Log::debugOutputLevel() | 186 | Sink::Log::DebugLevel Sink::Log::debugOutputLevel() |
180 | { | 187 | { |
181 | return static_cast<Sink::Log::DebugLevel>(config()->value("level", Sink::Log::Log).toInt()); | 188 | return static_cast<Sink::Log::DebugLevel>(config().value("level", Sink::Log::Log).toInt()); |
182 | } | 189 | } |
183 | 190 | ||
184 | void Sink::Log::setDebugOutputFilter(FilterType type, const QByteArrayList &filter) | 191 | void Sink::Log::setDebugOutputFilter(FilterType type, const QByteArrayList &filter) |
185 | { | 192 | { |
186 | switch (type) { | 193 | switch (type) { |
187 | case ApplicationName: | 194 | case ApplicationName: |
188 | config()->setValue("applicationfilter", QVariant::fromValue(filter)); | 195 | config().setValue("applicationfilter", QVariant::fromValue(filter)); |
189 | break; | 196 | break; |
190 | case Area: | 197 | case Area: |
191 | config()->setValue("areafilter", QVariant::fromValue(filter)); | 198 | config().setValue("areafilter", QVariant::fromValue(filter)); |
192 | break; | 199 | break; |
193 | } | 200 | } |
194 | } | 201 | } |
@@ -197,9 +204,9 @@ QByteArrayList Sink::Log::debugOutputFilter(FilterType type) | |||
197 | { | 204 | { |
198 | switch (type) { | 205 | switch (type) { |
199 | case ApplicationName: | 206 | case ApplicationName: |
200 | return config()->value("applicationfilter").value<QByteArrayList>(); | 207 | return config().value("applicationfilter").value<QByteArrayList>(); |
201 | case Area: | 208 | case Area: |
202 | return config()->value("areafilter").value<QByteArrayList>(); | 209 | return config().value("areafilter").value<QByteArrayList>(); |
203 | default: | 210 | default: |
204 | return QByteArrayList(); | 211 | return QByteArrayList(); |
205 | } | 212 | } |
@@ -207,12 +214,12 @@ QByteArrayList Sink::Log::debugOutputFilter(FilterType type) | |||
207 | 214 | ||
208 | void Sink::Log::setDebugOutputFields(const QByteArrayList &output) | 215 | void Sink::Log::setDebugOutputFields(const QByteArrayList &output) |
209 | { | 216 | { |
210 | config()->setValue("outputfields", QVariant::fromValue(output)); | 217 | config().setValue("outputfields", QVariant::fromValue(output)); |
211 | } | 218 | } |
212 | 219 | ||
213 | QByteArrayList Sink::Log::debugOutputFields() | 220 | QByteArrayList Sink::Log::debugOutputFields() |
214 | { | 221 | { |
215 | return config()->value("outputfields").value<QByteArrayList>(); | 222 | return config().value("outputfields").value<QByteArrayList>(); |
216 | } | 223 | } |
217 | 224 | ||
218 | static QByteArray getProgramName() | 225 | static QByteArray getProgramName() |
@@ -277,14 +284,16 @@ static bool containsItemStartingWith(const QByteArray &pattern, const QByteArray | |||
277 | for (const auto &item : list) { | 284 | for (const auto &item : list) { |
278 | if (item.startsWith('*')) { | 285 | if (item.startsWith('*')) { |
279 | auto stripped = item.mid(1); | 286 | auto stripped = item.mid(1); |
280 | stripped.endsWith('*'); | 287 | if (stripped.endsWith('*')) { |
281 | stripped.chop(1); | 288 | stripped.chop(1); |
289 | } | ||
282 | if (pattern.contains(stripped)) { | 290 | if (pattern.contains(stripped)) { |
283 | return true; | 291 | return true; |
284 | } | 292 | } |
285 | } | 293 | } else { |
286 | if (pattern.startsWith(item)) { | 294 | if (pattern.contains(item)) { |
287 | return true; | 295 | return true; |
296 | } | ||
288 | } | 297 | } |
289 | } | 298 | } |
290 | return false; | 299 | return false; |
@@ -300,25 +309,51 @@ static bool caseInsensitiveContains(const QByteArray &pattern, const QByteArrayL | |||
300 | return false; | 309 | return false; |
301 | } | 310 | } |
302 | 311 | ||
303 | QDebug Sink::Log::debugStream(DebugLevel debugLevel, int line, const char *file, const char *function, const char *debugArea, const char *debugComponent) | 312 | static QByteArray getFileName(const char *file) |
304 | { | 313 | { |
305 | static NullStream nullstream; | 314 | static char sep = QDir::separator().toLatin1(); |
306 | if (debugLevel < debugOutputLevel()) { | 315 | auto filename = QByteArray(file).split(sep).last(); |
307 | return QDebug(&nullstream); | 316 | return filename.split('.').first(); |
308 | } | 317 | } |
309 | 318 | ||
319 | static QString assembleDebugArea(const char *debugArea, const char *debugComponent, const char *file) | ||
320 | { | ||
310 | if (sPrimaryComponent.isEmpty()) { | 321 | if (sPrimaryComponent.isEmpty()) { |
311 | sPrimaryComponent = getProgramName(); | 322 | sPrimaryComponent = getProgramName(); |
312 | } | 323 | } |
313 | QString fullDebugArea = sPrimaryComponent + "." + (debugComponent ? (QString::fromLatin1(debugComponent) + ".") : "") + (debugArea ? QString::fromLatin1(debugArea) : ""); | 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 | } | ||
329 | |||
330 | static bool isFiltered(DebugLevel debugLevel, const QByteArray &fullDebugArea) | ||
331 | { | ||
332 | if (debugLevel < debugOutputLevel()) { | ||
333 | return true; | ||
334 | } | ||
335 | const auto areas = debugOutputFilter(Sink::Log::Area); | ||
336 | if ((debugLevel <= Sink::Log::Trace) && !areas.isEmpty()) { | ||
337 | if (!containsItemStartingWith(fullDebugArea, areas)) { | ||
338 | return true; | ||
339 | } | ||
340 | } | ||
341 | return false; | ||
342 | } | ||
314 | 343 | ||
344 | bool Sink::Log::isFiltered(DebugLevel debugLevel, const char *debugArea, const char *debugComponent, const char *file) | ||
345 | { | ||
346 | return isFiltered(debugLevel, assembleDebugArea(debugArea, debugComponent, file).toLatin1()); | ||
347 | } | ||
348 | |||
349 | QDebug Sink::Log::debugStream(DebugLevel debugLevel, int line, const char *file, const char *function, const char *debugArea, const char *debugComponent) | ||
350 | { | ||
351 | const auto fullDebugArea = assembleDebugArea(debugArea, debugComponent, file); | ||
315 | collectDebugArea(fullDebugArea); | 352 | collectDebugArea(fullDebugArea); |
316 | 353 | ||
317 | auto areas = debugOutputFilter(Sink::Log::Area); | 354 | static NullStream nullstream; |
318 | if (debugLevel <= Sink::Log::Trace && !areas.isEmpty()) { | 355 | if (isFiltered(debugLevel, fullDebugArea.toLatin1())) { |
319 | if (!containsItemStartingWith(fullDebugArea.toUtf8(), areas)) { | 356 | return QDebug(&nullstream); |
320 | return QDebug(&nullstream); | ||
321 | } | ||
322 | } | 357 | } |
323 | 358 | ||
324 | QString prefix; | 359 | QString prefix; |
diff --git a/common/log.h b/common/log.h index e85856a..8266fdb 100644 --- a/common/log.h +++ b/common/log.h | |||
@@ -85,30 +85,35 @@ SINK_EXPORT inline QDebug operator<<(QDebug d, const TraceTime &time) | |||
85 | d << time.time << "[ms]"; | 85 | d << time.time << "[ms]"; |
86 | return d; | 86 | return d; |
87 | } | 87 | } |
88 | |||
89 | SINK_EXPORT bool isFiltered(DebugLevel debugLevel, const char *debugArea, const char *debugComponent, const char *file); | ||
90 | |||
88 | } | 91 | } |
89 | } | 92 | } |
90 | 93 | ||
91 | static const char *getComponentName() { return nullptr; } | 94 | static const char *getComponentName() { return nullptr; } |
92 | 95 | ||
93 | #define Trace_area(AREA) Sink::Log::debugStream(Sink::Log::DebugLevel::Trace, __LINE__, __FILE__, Q_FUNC_INFO, AREA) | 96 | #define SINK_DEBUG_STREAM_IMPL(LEVEL, AREA, COMPONENT) if (!Sink::Log::isFiltered(LEVEL, AREA, COMPONENT, __FILE__)) Sink::Log::debugStream(LEVEL, __LINE__, __FILE__, Q_FUNC_INFO, AREA, COMPONENT) |
94 | #define Log_area(AREA) Sink::Log::debugStream(Sink::Log::DebugLevel::Log, __LINE__, __FILE__, Q_FUNC_INFO, AREA) | 97 | |
95 | #define Warning_area(AREA) Sink::Log::debugStream(Sink::Log::DebugLevel::Warning, __LINE__, __FILE__, Q_FUNC_INFO, AREA) | 98 | #define Trace_area(AREA) SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Trace, AREA, nullptr) |
96 | #define Error_area(AREA) Sink::Log::debugStream(Sink::Log::DebugLevel::Error, __LINE__, __FILE__, Q_FUNC_INFO, AREA) | 99 | #define Log_area(AREA) SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Log, AREA, nullptr) |
97 | 100 | #define Warning_area(AREA) SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Warning, AREA, nullptr) | |
98 | #define SinkTrace_(COMPONENT, AREA) Sink::Log::debugStream(Sink::Log::DebugLevel::Trace, __LINE__, __FILE__, Q_FUNC_INFO, AREA, COMPONENT) | 101 | #define Error_area(AREA) SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Error, AREA, nullptr) |
99 | #define SinkLog_(COMPONENT, AREA) Sink::Log::debugStream(Sink::Log::DebugLevel::Log, __LINE__, __FILE__, Q_FUNC_INFO, AREA, COMPONENT) | 102 | |
100 | #define SinkWarning_(COMPONENT, AREA) Sink::Log::debugStream(Sink::Log::DebugLevel::Warning, __LINE__, __FILE__, Q_FUNC_INFO, AREA, COMPONENT) | 103 | #define SinkTrace_(COMPONENT, AREA) SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Trace, AREA, COMPONENT) |
101 | #define SinkError_(COMPONENT, AREA) Sink::Log::debugStream(Sink::Log::DebugLevel::Error, __LINE__, __FILE__, Q_FUNC_INFO, AREA, COMPONENT) | 104 | #define SinkLog_(COMPONENT, AREA) SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Log, AREA, COMPONENT) |
102 | 105 | #define SinkWarning_(COMPONENT, AREA) SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Warning, AREA, COMPONENT) | |
103 | #define SinkTrace() Sink::Log::debugStream(Sink::Log::DebugLevel::Trace, __LINE__, __FILE__, Q_FUNC_INFO, s_sinkDebugArea, getComponentName()) | 106 | #define SinkError_(COMPONENT, AREA) SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Error, AREA, COMPONENT) |
104 | #define SinkLog() Sink::Log::debugStream(Sink::Log::DebugLevel::Log, __LINE__, __FILE__, Q_FUNC_INFO, s_sinkDebugArea, getComponentName()) | 107 | |
105 | #define SinkWarning() Sink::Log::debugStream(Sink::Log::DebugLevel::Warning, __LINE__, __FILE__, Q_FUNC_INFO, s_sinkDebugArea, getComponentName()) | 108 | #define SinkTrace() SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Trace, nullptr, getComponentName()) |
106 | #define SinkError() Sink::Log::debugStream(Sink::Log::DebugLevel::Error, __LINE__, __FILE__, Q_FUNC_INFO, s_sinkDebugArea, getComponentName()) | 109 | #define SinkLog() SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Log, nullptr, getComponentName()) |
107 | 110 | #define SinkWarning() SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Warning, nullptr, getComponentName()) | |
108 | #define SinkTraceCtx(CTX) Sink::Log::debugStream(Sink::Log::DebugLevel::Trace, __LINE__, __FILE__, Q_FUNC_INFO, CTX.name) | 111 | #define SinkError() SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Error, nullptr, getComponentName()) |
109 | #define SinkLogCtx(CTX) Sink::Log::debugStream(Sink::Log::DebugLevel::Log, __LINE__, __FILE__, Q_FUNC_INFO, CTX.name) | 112 | |
110 | #define SinkWarningCtx(CTX) Sink::Log::debugStream(Sink::Log::DebugLevel::Warning, __LINE__, __FILE__, Q_FUNC_INFO, CTX.name) | 113 | #define SinkTraceCtx(CTX) SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Trace, CTX.name, nullptr) |
111 | #define SinkErrorCtx(CTX) Sink::Log::debugStream(Sink::Log::DebugLevel::Error, __LINE__, __FILE__, Q_FUNC_INFO, CTX.name) | 114 | #define SinkLogCtx(CTX) SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Log, CTX.name, nullptr) |
115 | #define SinkWarningCtx(CTX) SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Warning, CTX.name, nullptr) | ||
116 | #define SinkErrorCtx(CTX) SINK_DEBUG_STREAM_IMPL(Sink::Log::DebugLevel::Error, CTX.name, nullptr) | ||
112 | 117 | ||
113 | #define SINK_DEBUG_AREA(AREA) static constexpr const char* s_sinkDebugArea{AREA}; | 118 | #define SINK_DEBUG_AREA(AREA) static constexpr const char* s_sinkDebugArea{AREA}; |
114 | #define SINK_DEBUG_COMPONENT(COMPONENT) const char* getComponentName() const { return COMPONENT; }; | 119 | #define SINK_DEBUG_COMPONENT(COMPONENT) const char* getComponentName() const { return COMPONENT; }; |
diff --git a/common/mail/threadindexer.cpp b/common/mail/threadindexer.cpp index d91ab5f..4171d85 100644 --- a/common/mail/threadindexer.cpp +++ b/common/mail/threadindexer.cpp | |||
@@ -21,74 +21,15 @@ | |||
21 | #include "typeindex.h" | 21 | #include "typeindex.h" |
22 | #include "log.h" | 22 | #include "log.h" |
23 | 23 | ||
24 | SINK_DEBUG_AREA("threadindex") | ||
25 | |||
26 | using namespace Sink; | 24 | using namespace Sink; |
27 | using namespace Sink::ApplicationDomain; | 25 | using namespace Sink::ApplicationDomain; |
28 | 26 | ||
29 | static QString stripOffPrefixes(const QString &subject) | ||
30 | { | ||
31 | //TODO this hardcoded list is probably not good enough (especially regarding internationalization) | ||
32 | //TODO this whole routine, including internationalized re/fwd ... should go into some library. | ||
33 | //We'll require the same for generating reply/forward subjects in kube | ||
34 | static QStringList defaultReplyPrefixes = QStringList() << QLatin1String("Re\\s*:") | ||
35 | << QLatin1String("Re\\[\\d+\\]:") | ||
36 | << QLatin1String("Re\\d+:"); | ||
37 | |||
38 | static QStringList defaultForwardPrefixes = QStringList() << QLatin1String("Fwd:") | ||
39 | << QLatin1String("FW:"); | ||
40 | |||
41 | QStringList replyPrefixes; // = GlobalSettings::self()->replyPrefixes(); | ||
42 | if (replyPrefixes.isEmpty()) { | ||
43 | replyPrefixes = defaultReplyPrefixes; | ||
44 | } | ||
45 | |||
46 | QStringList forwardPrefixes; // = GlobalSettings::self()->forwardPrefixes(); | ||
47 | if (forwardPrefixes.isEmpty()) { | ||
48 | forwardPrefixes = defaultReplyPrefixes; | ||
49 | } | ||
50 | |||
51 | const QStringList prefixRegExps = replyPrefixes + forwardPrefixes; | ||
52 | |||
53 | // construct a big regexp that | ||
54 | // 1. is anchored to the beginning of str (sans whitespace) | ||
55 | // 2. matches at least one of the part regexps in prefixRegExps | ||
56 | const QString bigRegExp = QString::fromLatin1("^(?:\\s+|(?:%1))+\\s*").arg(prefixRegExps.join(QLatin1String(")|(?:"))); | ||
57 | |||
58 | static QString regExpPattern; | ||
59 | static QRegExp regExp; | ||
60 | |||
61 | regExp.setCaseSensitivity(Qt::CaseInsensitive); | ||
62 | if (regExpPattern != bigRegExp) { | ||
63 | // the prefixes have changed, so update the regexp | ||
64 | regExpPattern = bigRegExp; | ||
65 | regExp.setPattern(regExpPattern); | ||
66 | } | ||
67 | |||
68 | if(regExp.isValid()) { | ||
69 | QString tmp = subject; | ||
70 | if (regExp.indexIn( tmp ) == 0) { | ||
71 | return tmp.remove(0, regExp.matchedLength()); | ||
72 | } | ||
73 | } else { | ||
74 | SinkWarning() << "bigRegExp = \"" | ||
75 | << bigRegExp << "\"\n" | ||
76 | << "prefix regexp is invalid!"; | ||
77 | } | ||
78 | |||
79 | return subject; | ||
80 | } | ||
81 | |||
82 | |||
83 | void ThreadIndexer::updateThreadingIndex(const QByteArray &identifier, const ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction) | 27 | void ThreadIndexer::updateThreadingIndex(const QByteArray &identifier, const ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction) |
84 | { | 28 | { |
85 | auto messageId = entity.getProperty(Mail::MessageId::name); | 29 | auto messageId = entity.getProperty(Mail::MessageId::name); |
86 | auto parentMessageId = entity.getProperty(Mail::ParentMessageId::name); | 30 | auto parentMessageId = entity.getProperty(Mail::ParentMessageId::name); |
87 | const auto subject = entity.getProperty(Mail::Subject::name); | ||
88 | const auto normalizedSubject = stripOffPrefixes(subject.toString()).toUtf8(); | ||
89 | if (messageId.toByteArray().isEmpty()) { | 31 | if (messageId.toByteArray().isEmpty()) { |
90 | SinkWarning() << "Found an email without messageId. This is illegal and threading will break. Entity id: " << identifier; | 32 | SinkWarning() << "Found an email without messageId. This is illegal and threading will break. Entity id: " << identifier; |
91 | SinkWarning() << "Subject: " << subject; | ||
92 | } | 33 | } |
93 | 34 | ||
94 | QVector<QByteArray> thread; | 35 | QVector<QByteArray> thread; |
@@ -101,18 +42,9 @@ void ThreadIndexer::updateThreadingIndex(const QByteArray &identifier, const App | |||
101 | thread = index().secondaryLookup<Mail::MessageId, Mail::ThreadId>(parentMessageId); | 42 | thread = index().secondaryLookup<Mail::MessageId, Mail::ThreadId>(parentMessageId); |
102 | SinkTrace() << "Found parent: " << thread; | 43 | SinkTrace() << "Found parent: " << thread; |
103 | } | 44 | } |
104 | |||
105 | if (thread.isEmpty()) { | 45 | if (thread.isEmpty()) { |
106 | //Try to lookup the thread by subject if not empty | 46 | thread << QUuid::createUuid().toByteArray(); |
107 | if ( !normalizedSubject.isEmpty()) { | 47 | SinkTrace() << "Created a new thread: " << thread; |
108 | thread = index().secondaryLookup<Mail::Subject, Mail::ThreadId>(normalizedSubject); | ||
109 | } | ||
110 | if (thread.isEmpty()) { | ||
111 | thread << QUuid::createUuid().toByteArray(); | ||
112 | SinkTrace() << "Created a new thread: " << thread; | ||
113 | } else { | ||
114 | SinkTrace() << "Found thread by subject: " << thread; | ||
115 | } | ||
116 | } | 48 | } |
117 | 49 | ||
118 | Q_ASSERT(!thread.isEmpty()); | 50 | Q_ASSERT(!thread.isEmpty()); |
@@ -124,9 +56,6 @@ void ThreadIndexer::updateThreadingIndex(const QByteArray &identifier, const App | |||
124 | } | 56 | } |
125 | index().index<Mail::MessageId, Mail::ThreadId>(messageId, thread.first(), transaction); | 57 | index().index<Mail::MessageId, Mail::ThreadId>(messageId, thread.first(), transaction); |
126 | index().index<Mail::ThreadId, Mail::MessageId>(thread.first(), messageId, transaction); | 58 | index().index<Mail::ThreadId, Mail::MessageId>(thread.first(), messageId, transaction); |
127 | if (!normalizedSubject.isEmpty()) { | ||
128 | index().index<Mail::Subject, Mail::ThreadId>(normalizedSubject, thread.first(), transaction); | ||
129 | } | ||
130 | } | 59 | } |
131 | 60 | ||
132 | 61 | ||
@@ -142,6 +71,15 @@ void ThreadIndexer::modify(const ApplicationDomain::ApplicationDomainType &old, | |||
142 | 71 | ||
143 | void ThreadIndexer::remove(const ApplicationDomain::ApplicationDomainType &entity) | 72 | void ThreadIndexer::remove(const ApplicationDomain::ApplicationDomainType &entity) |
144 | { | 73 | { |
74 | auto messageId = entity.getProperty(Mail::MessageId::name); | ||
75 | auto thread = index().secondaryLookup<Mail::MessageId, Mail::ThreadId>(messageId); | ||
76 | index().unindex<Mail::MessageId, Mail::ThreadId>(messageId.toByteArray(), thread.first(), transaction()); | ||
77 | index().unindex<Mail::ThreadId, Mail::MessageId>(thread.first(), messageId.toByteArray(), transaction()); | ||
78 | } | ||
145 | 79 | ||
80 | QMap<QByteArray, int> ThreadIndexer::databases() | ||
81 | { | ||
82 | return {{"mail.index.messageIdthreadId", 1}, | ||
83 | {"mail.index.threadIdmessageId", 1}}; | ||
146 | } | 84 | } |
147 | 85 | ||
diff --git a/common/mail/threadindexer.h b/common/mail/threadindexer.h index 064ae71..60d0863 100644 --- a/common/mail/threadindexer.h +++ b/common/mail/threadindexer.h | |||
@@ -29,6 +29,7 @@ public: | |||
29 | virtual void add(const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE; | 29 | virtual void add(const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE; |
30 | virtual void modify(const ApplicationDomain::ApplicationDomainType &old, const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE; | 30 | virtual void modify(const ApplicationDomain::ApplicationDomainType &old, const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE; |
31 | virtual void remove(const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE; | 31 | virtual void remove(const ApplicationDomain::ApplicationDomainType &entity) Q_DECL_OVERRIDE; |
32 | static QMap<QByteArray, int> databases(); | ||
32 | private: | 33 | private: |
33 | void updateThreadingIndex(const QByteArray &identifier, const ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction); | 34 | void updateThreadingIndex(const QByteArray &identifier, const ApplicationDomain::ApplicationDomainType &entity, Sink::Storage::DataStore::Transaction &transaction); |
34 | }; | 35 | }; |
diff --git a/common/mailpreprocessor.cpp b/common/mailpreprocessor.cpp index dff3b3d..5c54fbc 100644 --- a/common/mailpreprocessor.cpp +++ b/common/mailpreprocessor.cpp | |||
@@ -29,8 +29,6 @@ | |||
29 | 29 | ||
30 | using namespace Sink; | 30 | using namespace Sink; |
31 | 31 | ||
32 | SINK_DEBUG_AREA("mailpreprocessor") | ||
33 | |||
34 | QString MailPropertyExtractor::getFilePathFromMimeMessagePath(const QString &s) const | 32 | QString MailPropertyExtractor::getFilePathFromMimeMessagePath(const QString &s) const |
35 | { | 33 | { |
36 | return s; | 34 | return s; |
diff --git a/common/messagequeue.cpp b/common/messagequeue.cpp index 6e79d89..362ddfd 100644 --- a/common/messagequeue.cpp +++ b/common/messagequeue.cpp | |||
@@ -3,8 +3,6 @@ | |||
3 | #include <QDebug> | 3 | #include <QDebug> |
4 | #include <log.h> | 4 | #include <log.h> |
5 | 5 | ||
6 | SINK_DEBUG_AREA("messagequeue") | ||
7 | |||
8 | MessageQueue::MessageQueue(const QString &storageRoot, const QString &name) : mStorage(storageRoot, name, Sink::Storage::DataStore::ReadWrite) | 6 | MessageQueue::MessageQueue(const QString &storageRoot, const QString &name) : mStorage(storageRoot, name, Sink::Storage::DataStore::ReadWrite) |
9 | { | 7 | { |
10 | } | 8 | } |
diff --git a/common/modelresult.cpp b/common/modelresult.cpp index b12216b..58703ab 100644 --- a/common/modelresult.cpp +++ b/common/modelresult.cpp | |||
@@ -224,7 +224,6 @@ QModelIndex ModelResult<T, Ptr>::index(int row, int column, const QModelIndex &p | |||
224 | return createIndex(row, column, childId); | 224 | return createIndex(row, column, childId); |
225 | } | 225 | } |
226 | SinkWarningCtx(mLogCtx) << "Index not available " << row << column << parent; | 226 | SinkWarningCtx(mLogCtx) << "Index not available " << row << column << parent; |
227 | Q_ASSERT(false); | ||
228 | return QModelIndex(); | 227 | return QModelIndex(); |
229 | } | 228 | } |
230 | 229 | ||
diff --git a/common/notification.cpp b/common/notification.cpp index e688b6d..da31e20 100644 --- a/common/notification.cpp +++ b/common/notification.cpp | |||
@@ -48,8 +48,8 @@ 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(); |
55 | } | 55 | } |
diff --git a/common/notification.h b/common/notification.h index f5379fd..30e240c 100644 --- a/common/notification.h +++ b/common/notification.h | |||
@@ -56,6 +56,8 @@ public: | |||
56 | QString message; | 56 | QString message; |
57 | //A return code. Zero typically indicates success. | 57 | //A return code. Zero typically indicates success. |
58 | int code = 0; | 58 | int code = 0; |
59 | int progress = 0; | ||
60 | int total = 0; | ||
59 | QByteArray resource; | 61 | QByteArray resource; |
60 | }; | 62 | }; |
61 | } | 63 | } |
diff --git a/common/pipeline.cpp b/common/pipeline.cpp index f5cf995..f29c7db 100644 --- a/common/pipeline.cpp +++ b/common/pipeline.cpp | |||
@@ -283,6 +283,8 @@ KAsync::Job<qint64> Pipeline::modifiedEntity(void const *command, size_t size) | |||
283 | case Preprocessor::DropModification: | 283 | case Preprocessor::DropModification: |
284 | SinkTraceCtx(d->logCtx) << "Dropping modification"; | 284 | SinkTraceCtx(d->logCtx) << "Dropping modification"; |
285 | return KAsync::error<qint64>(0); | 285 | return KAsync::error<qint64>(0); |
286 | case Preprocessor::NoAction: | ||
287 | case Preprocessor::DeleteEntity: | ||
286 | default: | 288 | default: |
287 | break; | 289 | break; |
288 | } | 290 | } |
@@ -301,8 +303,8 @@ KAsync::Job<qint64> Pipeline::modifiedEntity(void const *command, size_t size) | |||
301 | SinkTraceCtx(d->logCtx) << "Move of " << current.identifier() << "was successfull"; | 303 | SinkTraceCtx(d->logCtx) << "Move of " << current.identifier() << "was successfull"; |
302 | if (isMove) { | 304 | if (isMove) { |
303 | flatbuffers::FlatBufferBuilder fbb; | 305 | flatbuffers::FlatBufferBuilder fbb; |
304 | auto entityId = fbb.CreateString(current.identifier()); | 306 | auto entityId = fbb.CreateString(current.identifier().toStdString()); |
305 | auto type = fbb.CreateString(bufferType); | 307 | auto type = fbb.CreateString(bufferType.toStdString()); |
306 | auto location = Sink::Commands::CreateDeleteEntity(fbb, current.revision(), entityId, type, true); | 308 | auto location = Sink::Commands::CreateDeleteEntity(fbb, current.revision(), entityId, type, true); |
307 | Sink::Commands::FinishDeleteEntityBuffer(fbb, location); | 309 | Sink::Commands::FinishDeleteEntityBuffer(fbb, location); |
308 | const auto data = BufferUtils::extractBuffer(fbb); | 310 | const auto data = BufferUtils::extractBuffer(fbb); |
@@ -448,8 +450,8 @@ void Preprocessor::createEntity(const Sink::ApplicationDomain::ApplicationDomain | |||
448 | const auto entityBuffer = BufferUtils::extractBuffer(entityFbb); | 450 | const auto entityBuffer = BufferUtils::extractBuffer(entityFbb); |
449 | 451 | ||
450 | flatbuffers::FlatBufferBuilder fbb; | 452 | flatbuffers::FlatBufferBuilder fbb; |
451 | auto entityId = fbb.CreateString(entity.identifier()); | 453 | auto entityId = fbb.CreateString(entity.identifier().toStdString()); |
452 | auto type = fbb.CreateString(typeName); | 454 | auto type = fbb.CreateString(typeName.toStdString()); |
453 | auto delta = Sink::EntityBuffer::appendAsVector(fbb, entityBuffer.constData(), entityBuffer.size()); | 455 | auto delta = Sink::EntityBuffer::appendAsVector(fbb, entityBuffer.constData(), entityBuffer.size()); |
454 | auto location = Sink::Commands::CreateCreateEntity(fbb, entityId, type, delta); | 456 | auto location = Sink::Commands::CreateCreateEntity(fbb, entityId, type, delta); |
455 | Sink::Commands::FinishCreateEntityBuffer(fbb, location); | 457 | Sink::Commands::FinishCreateEntityBuffer(fbb, location); |
diff --git a/common/propertymapper.cpp b/common/propertymapper.cpp index c72cf31..dbf93a3 100644 --- a/common/propertymapper.cpp +++ b/common/propertymapper.cpp | |||
@@ -21,6 +21,7 @@ | |||
21 | 21 | ||
22 | #include "applicationdomaintype.h" | 22 | #include "applicationdomaintype.h" |
23 | #include <QDateTime> | 23 | #include <QDateTime> |
24 | #include <QDataStream> | ||
24 | #include "mail_generated.h" | 25 | #include "mail_generated.h" |
25 | #include "contact_generated.h" | 26 | #include "contact_generated.h" |
26 | 27 | ||
@@ -57,7 +58,9 @@ template <> | |||
57 | flatbuffers::uoffset_t variantToProperty<QByteArray>(const QVariant &property, flatbuffers::FlatBufferBuilder &fbb) | 58 | flatbuffers::uoffset_t variantToProperty<QByteArray>(const QVariant &property, flatbuffers::FlatBufferBuilder &fbb) |
58 | { | 59 | { |
59 | if (property.isValid()) { | 60 | if (property.isValid()) { |
60 | return fbb.CreateString(property.toByteArray().toStdString()).o; | 61 | const auto ba = property.toByteArray(); |
62 | const auto s = fbb.CreateString(ba.constData(), ba.size()); | ||
63 | return s.o; | ||
61 | } | 64 | } |
62 | return 0; | 65 | return 0; |
63 | } | 66 | } |
@@ -66,7 +69,10 @@ template <> | |||
66 | flatbuffers::uoffset_t variantToProperty<QDateTime>(const QVariant &property, flatbuffers::FlatBufferBuilder &fbb) | 69 | flatbuffers::uoffset_t variantToProperty<QDateTime>(const QVariant &property, flatbuffers::FlatBufferBuilder &fbb) |
67 | { | 70 | { |
68 | if (property.isValid()) { | 71 | if (property.isValid()) { |
69 | return fbb.CreateString(property.toDateTime().toString().toStdString()).o; | 72 | QByteArray ba; |
73 | QDataStream ds(&ba, QIODevice::WriteOnly); | ||
74 | ds << property.toDateTime(); | ||
75 | return fbb.CreateString(ba.toStdString()).o; | ||
70 | } | 76 | } |
71 | return 0; | 77 | return 0; |
72 | } | 78 | } |
@@ -131,7 +137,7 @@ QString propertyToString(const flatbuffers::String *property) | |||
131 | { | 137 | { |
132 | if (property) { | 138 | if (property) { |
133 | // We have to copy the memory, otherwise it would become eventually invalid | 139 | // We have to copy the memory, otherwise it would become eventually invalid |
134 | return QString::fromStdString(property->c_str()); | 140 | return QString::fromStdString(property->str()); |
135 | } | 141 | } |
136 | return QString(); | 142 | return QString(); |
137 | } | 143 | } |
@@ -141,7 +147,7 @@ QVariant propertyToVariant<QString>(const flatbuffers::String *property) | |||
141 | { | 147 | { |
142 | if (property) { | 148 | if (property) { |
143 | // We have to copy the memory, otherwise it would become eventually invalid | 149 | // We have to copy the memory, otherwise it would become eventually invalid |
144 | return QString::fromStdString(property->c_str()); | 150 | return QString::fromStdString(property->str()); |
145 | } | 151 | } |
146 | return QVariant(); | 152 | return QVariant(); |
147 | } | 153 | } |
@@ -151,7 +157,7 @@ QVariant propertyToVariant<Sink::ApplicationDomain::BLOB>(const flatbuffers::Str | |||
151 | { | 157 | { |
152 | if (property) { | 158 | if (property) { |
153 | // We have to copy the memory, otherwise it would become eventually invalid | 159 | // We have to copy the memory, otherwise it would become eventually invalid |
154 | auto s = QString::fromStdString(property->c_str()); | 160 | auto s = QString::fromStdString(property->str()); |
155 | auto ext = s.endsWith(":ext"); | 161 | auto ext = s.endsWith(":ext"); |
156 | s.chop(4); | 162 | s.chop(4); |
157 | 163 | ||
@@ -167,7 +173,7 @@ QVariant propertyToVariant<Sink::ApplicationDomain::Reference>(const flatbuffers | |||
167 | { | 173 | { |
168 | if (property) { | 174 | if (property) { |
169 | // We have to copy the memory, otherwise it would become eventually invalid | 175 | // We have to copy the memory, otherwise it would become eventually invalid |
170 | return QVariant::fromValue(Sink::ApplicationDomain::Reference{QString::fromStdString(property->c_str()).toUtf8()}); | 176 | return QVariant::fromValue(Sink::ApplicationDomain::Reference{QString::fromStdString(property->str()).toUtf8()}); |
171 | } | 177 | } |
172 | return QVariant(); | 178 | return QVariant(); |
173 | } | 179 | } |
@@ -177,7 +183,7 @@ QVariant propertyToVariant<QByteArray>(const flatbuffers::String *property) | |||
177 | { | 183 | { |
178 | if (property) { | 184 | if (property) { |
179 | // We have to copy the memory, otherwise it would become eventually invalid | 185 | // We have to copy the memory, otherwise it would become eventually invalid |
180 | return QString::fromStdString(property->c_str()).toUtf8(); | 186 | return QByteArray(property->c_str(), property->Length()); |
181 | } | 187 | } |
182 | return QVariant(); | 188 | return QVariant(); |
183 | } | 189 | } |
@@ -199,7 +205,7 @@ QVariant propertyToVariant<QByteArrayList>(const flatbuffers::Vector<flatbuffers | |||
199 | QByteArrayList list; | 205 | QByteArrayList list; |
200 | for (auto it = property->begin(); it != property->end();) { | 206 | for (auto it = property->begin(); it != property->end();) { |
201 | // We have to copy the memory, otherwise it would become eventually invalid | 207 | // We have to copy the memory, otherwise it would become eventually invalid |
202 | list << QString::fromStdString((*it)->c_str()).toUtf8(); | 208 | list << QString::fromStdString((*it)->str()).toUtf8(); |
203 | it.operator++(); | 209 | it.operator++(); |
204 | } | 210 | } |
205 | return QVariant::fromValue(list); | 211 | return QVariant::fromValue(list); |
@@ -256,8 +262,11 @@ template <> | |||
256 | QVariant propertyToVariant<QDateTime>(const flatbuffers::String *property) | 262 | QVariant propertyToVariant<QDateTime>(const flatbuffers::String *property) |
257 | { | 263 | { |
258 | if (property) { | 264 | if (property) { |
259 | // We have to copy the memory, otherwise it would become eventually invalid | 265 | auto ba = QByteArray::fromRawData(property->c_str(), property->size()); |
260 | return QDateTime::fromString(QString::fromStdString(property->c_str())); | 266 | QDateTime dt; |
267 | QDataStream ds(&ba, QIODevice::ReadOnly); | ||
268 | ds >> dt; | ||
269 | return dt; | ||
261 | } | 270 | } |
262 | return QVariant(); | 271 | return QVariant(); |
263 | } | 272 | } |
diff --git a/common/propertymapper.h b/common/propertymapper.h index 9ea0b73..fd24278 100644 --- a/common/propertymapper.h +++ b/common/propertymapper.h | |||
@@ -65,13 +65,19 @@ QVariant SINK_EXPORT propertyToVariant(const flatbuffers::Vector<flatbuffers::Of | |||
65 | * a virtual method per property, the property mapper can be filled with accessors | 65 | * a virtual method per property, the property mapper can be filled with accessors |
66 | * that extract the properties from resource types. | 66 | * that extract the properties from resource types. |
67 | */ | 67 | */ |
68 | template <typename BufferType> | 68 | class PropertyMapper |
69 | class ReadPropertyMapper | ||
70 | { | 69 | { |
71 | public: | 70 | public: |
72 | virtual ~ReadPropertyMapper(){}; | 71 | virtual ~PropertyMapper(){}; |
73 | 72 | ||
74 | virtual QVariant getProperty(const QByteArray &key, BufferType const *buffer) const | 73 | template <typename T, typename Buffer, typename BufferBuilder, typename FunctionReturnValue, typename Arg> |
74 | void addMapping(FunctionReturnValue (Buffer::*f)() const, void (BufferBuilder::*f2)(Arg)) | ||
75 | { | ||
76 | addReadMapping<T, Buffer, FunctionReturnValue>(f); | ||
77 | addWriteMapping<T, BufferBuilder>(f2); | ||
78 | } | ||
79 | |||
80 | virtual QVariant getProperty(const QByteArray &key, void const *buffer) const | ||
75 | { | 81 | { |
76 | if (mReadAccessors.contains(key)) { | 82 | if (mReadAccessors.contains(key)) { |
77 | auto accessor = mReadAccessors.value(key); | 83 | auto accessor = mReadAccessors.value(key); |
@@ -79,163 +85,69 @@ public: | |||
79 | } | 85 | } |
80 | return QVariant(); | 86 | return QVariant(); |
81 | } | 87 | } |
82 | bool hasMapping(const QByteArray &key) const | ||
83 | { | ||
84 | return mReadAccessors.contains(key); | ||
85 | } | ||
86 | QList<QByteArray> availableProperties() const | ||
87 | { | ||
88 | return mReadAccessors.keys(); | ||
89 | } | ||
90 | void addMapping(const QByteArray &property, const std::function<QVariant(BufferType const *)> &mapping) | ||
91 | { | ||
92 | mReadAccessors.insert(property, mapping); | ||
93 | } | ||
94 | |||
95 | template <typename T, typename Buffer> | ||
96 | void addMapping(const flatbuffers::String *(Buffer::*f)() const) | ||
97 | { | ||
98 | addMapping(T::name, [f](Buffer const *buffer) -> QVariant { return propertyToVariant<typename T::Type>((buffer->*f)()); }); | ||
99 | } | ||
100 | |||
101 | template <typename T, typename Buffer> | ||
102 | void addMapping(uint8_t (Buffer::*f)() const) | ||
103 | { | ||
104 | addMapping(T::name, [f](Buffer const *buffer) -> QVariant { return propertyToVariant<typename T::Type>((buffer->*f)()); }); | ||
105 | } | ||
106 | |||
107 | template <typename T, typename Buffer> | ||
108 | void addMapping(bool (Buffer::*f)() const) | ||
109 | { | ||
110 | addMapping(T::name, [f](Buffer const *buffer) -> QVariant { return propertyToVariant<typename T::Type>((buffer->*f)()); }); | ||
111 | } | ||
112 | |||
113 | template <typename T, typename Buffer> | ||
114 | void addMapping(const flatbuffers::Vector<uint8_t> *(Buffer::*f)() const) | ||
115 | { | ||
116 | addMapping(T::name, [f](Buffer const *buffer) -> QVariant { return propertyToVariant<typename T::Type>((buffer->*f)()); }); | ||
117 | } | ||
118 | |||
119 | template <typename T, typename Buffer> | ||
120 | void addMapping(const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *(Buffer::*f)() const) | ||
121 | { | ||
122 | addMapping(T::name, [f](Buffer const *buffer) -> QVariant { return propertyToVariant<typename T::Type>((buffer->*f)()); }); | ||
123 | } | ||
124 | |||
125 | template <typename T, typename Buffer> | ||
126 | void addMapping(const Sink::ApplicationDomain::Buffer::MailContact *(Buffer::*f)() const) | ||
127 | { | ||
128 | addMapping(T::name, [f](Buffer const *buffer) -> QVariant { return propertyToVariant<typename T::Type>((buffer->*f)()); }); | ||
129 | } | ||
130 | |||
131 | template <typename T, typename Buffer> | ||
132 | void addMapping(const flatbuffers::Vector<flatbuffers::Offset<Sink::ApplicationDomain::Buffer::MailContact>> *(Buffer::*f)() const) | ||
133 | { | ||
134 | addMapping(T::name, [f](Buffer const *buffer) -> QVariant { return propertyToVariant<typename T::Type>((buffer->*f)()); }); | ||
135 | } | ||
136 | 88 | ||
137 | template <typename T, typename Buffer> | 89 | virtual void setProperty(const QByteArray &key, const QVariant &value, QList<std::function<void(void *builder)>> &builderCalls, flatbuffers::FlatBufferBuilder &fbb) const |
138 | void addMapping(const flatbuffers::Vector<flatbuffers::Offset<Sink::ApplicationDomain::Buffer::ContactEmail>> *(Buffer::*f)() const) | ||
139 | { | ||
140 | addMapping(T::name, [f](Buffer const *buffer) -> QVariant { return propertyToVariant<typename T::Type>((buffer->*f)()); }); | ||
141 | } | ||
142 | |||
143 | private: | ||
144 | QHash<QByteArray, std::function<QVariant(BufferType const *)>> mReadAccessors; | ||
145 | }; | ||
146 | |||
147 | template <typename BufferBuilder> | ||
148 | class WritePropertyMapper | ||
149 | { | ||
150 | public: | ||
151 | virtual ~WritePropertyMapper(){}; | ||
152 | |||
153 | virtual void setProperty(const QByteArray &key, const QVariant &value, QList<std::function<void(BufferBuilder &)>> &builderCalls, flatbuffers::FlatBufferBuilder &fbb) const | ||
154 | { | 90 | { |
155 | if (mWriteAccessors.contains(key)) { | 91 | if (mWriteAccessors.contains(key)) { |
156 | auto accessor = mWriteAccessors.value(key); | 92 | auto accessor = mWriteAccessors.value(key); |
157 | builderCalls << accessor(value, fbb); | 93 | builderCalls << accessor(value, fbb); |
158 | } | 94 | } |
159 | } | 95 | } |
96 | |||
160 | bool hasMapping(const QByteArray &key) const | 97 | bool hasMapping(const QByteArray &key) const |
161 | { | 98 | { |
162 | return mWriteAccessors.contains(key); | 99 | return mReadAccessors.contains(key); |
163 | } | ||
164 | void addMapping(const QByteArray &property, const std::function<std::function<void(BufferBuilder &)>(const QVariant &, flatbuffers::FlatBufferBuilder &)> &mapping) | ||
165 | { | ||
166 | mWriteAccessors.insert(property, mapping); | ||
167 | } | 100 | } |
168 | 101 | ||
169 | template <typename T> | 102 | QList<QByteArray> availableProperties() const |
170 | void addMapping(void (BufferBuilder::*f)(uint8_t)) | ||
171 | { | 103 | { |
172 | addMapping(T::name, [f](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(BufferBuilder &)> { | 104 | return mReadAccessors.keys(); |
173 | return [value, f](BufferBuilder &builder) { (builder.*f)(value.value<typename T::Type>()); }; | ||
174 | }); | ||
175 | } | 105 | } |
176 | 106 | ||
177 | template <typename T> | 107 | private: |
178 | void addMapping(void (BufferBuilder::*f)(bool)) | 108 | void addReadMapping(const QByteArray &property, const std::function<QVariant(void const *)> &mapping) |
179 | { | 109 | { |
180 | addMapping(T::name, [f](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(BufferBuilder &)> { | 110 | mReadAccessors.insert(property, mapping); |
181 | return [value, f](BufferBuilder &builder) { (builder.*f)(value.value<typename T::Type>()); }; | ||
182 | }); | ||
183 | } | 111 | } |
184 | 112 | ||
185 | template <typename T> | 113 | template <typename T, typename Buffer, typename FunctionReturnValue> |
186 | void addMapping(void (BufferBuilder::*f)(flatbuffers::Offset<flatbuffers::String>)) | 114 | void addReadMapping(FunctionReturnValue (Buffer::*f)() const) |
187 | { | 115 | { |
188 | addMapping(T::name, [f](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(BufferBuilder &)> { | 116 | addReadMapping(T::name, [f](void const *buffer) -> QVariant { return propertyToVariant<typename T::Type>((static_cast<const Buffer*>(buffer)->*f)()); }); |
189 | auto offset = variantToProperty<typename T::Type>(value, fbb); | ||
190 | return [offset, f](BufferBuilder &builder) { (builder.*f)(offset); }; | ||
191 | }); | ||
192 | } | 117 | } |
193 | 118 | ||
194 | template <typename T> | ||
195 | void addMapping(void (BufferBuilder::*f)(flatbuffers::Offset<flatbuffers::Vector<uint8_t>>)) | ||
196 | { | ||
197 | addMapping(T::name, [f](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(BufferBuilder &)> { | ||
198 | auto offset = variantToProperty<typename T::Type>(value, fbb); | ||
199 | return [offset, f](BufferBuilder &builder) { (builder.*f)(offset); }; | ||
200 | }); | ||
201 | } | ||
202 | 119 | ||
203 | template <typename T> | 120 | void addWriteMapping(const QByteArray &property, const std::function<std::function<void(void *builder)>(const QVariant &, flatbuffers::FlatBufferBuilder &)> &mapping) |
204 | void addMapping(void (BufferBuilder::*f)(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>>)) | ||
205 | { | 121 | { |
206 | addMapping(T::name, [f](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(BufferBuilder &)> { | 122 | mWriteAccessors.insert(property, mapping); |
207 | auto offset = variantToProperty<typename T::Type>(value, fbb); | ||
208 | return [offset, f](BufferBuilder &builder) { (builder.*f)(offset); }; | ||
209 | }); | ||
210 | } | 123 | } |
211 | 124 | ||
212 | template <typename T> | 125 | template <typename T, typename BufferBuilder> |
213 | void addMapping(void (BufferBuilder::*f)(flatbuffers::Offset<Sink::ApplicationDomain::Buffer::MailContact>)) | 126 | void addWriteMapping(void (BufferBuilder::*f)(uint8_t)) |
214 | { | 127 | { |
215 | addMapping(T::name, [f](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(BufferBuilder &)> { | 128 | addWriteMapping(T::name, [f](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(void *builder)> { |
216 | auto offset = variantToProperty<typename T::Type>(value, fbb); | 129 | return [value, f](void *builder) { (static_cast<BufferBuilder*>(builder)->*f)(value.value<typename T::Type>()); }; |
217 | return [offset, f](BufferBuilder &builder) { (builder.*f)(offset); }; | ||
218 | }); | 130 | }); |
219 | } | 131 | } |
220 | 132 | ||
221 | template <typename T> | 133 | template <typename T, typename BufferBuilder> |
222 | void addMapping(void (BufferBuilder::*f)(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Sink::ApplicationDomain::Buffer::MailContact>>>)) | 134 | void addWriteMapping(void (BufferBuilder::*f)(bool)) |
223 | { | 135 | { |
224 | addMapping(T::name, [f](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(BufferBuilder &)> { | 136 | addWriteMapping(T::name, [f](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(void *builder)> { |
225 | auto offset = variantToProperty<typename T::Type>(value, fbb); | 137 | return [value, f](void *builder) { (static_cast<BufferBuilder*>(builder)->*f)(value.value<typename T::Type>()); }; |
226 | return [offset, f](BufferBuilder &builder) { (builder.*f)(offset); }; | ||
227 | }); | 138 | }); |
228 | } | 139 | } |
229 | 140 | ||
230 | template <typename T> | 141 | template <typename T, typename BufferBuilder, typename Arg> |
231 | void addMapping(void (BufferBuilder::*f)(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Sink::ApplicationDomain::Buffer::ContactEmail>>>)) | 142 | void addWriteMapping(void (BufferBuilder::*f)(flatbuffers::Offset<Arg>)) |
232 | { | 143 | { |
233 | addMapping(T::name, [f](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(BufferBuilder &)> { | 144 | addWriteMapping(T::name, [f](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(void *builder)> { |
234 | auto offset = variantToProperty<typename T::Type>(value, fbb); | 145 | auto offset = variantToProperty<typename T::Type>(value, fbb); |
235 | return [offset, f](BufferBuilder &builder) { (builder.*f)(offset); }; | 146 | return [offset, f](void *builder) { (static_cast<BufferBuilder*>(builder)->*f)(offset); }; |
236 | }); | 147 | }); |
237 | } | 148 | } |
238 | 149 | ||
239 | private: | 150 | QHash<QByteArray, std::function<QVariant(void const *)>> mReadAccessors; |
240 | QHash<QByteArray, std::function<std::function<void(BufferBuilder &)>(const QVariant &, flatbuffers::FlatBufferBuilder &)>> mWriteAccessors; | 151 | QHash<QByteArray, std::function<std::function<void(void *builder)>(const QVariant &, flatbuffers::FlatBufferBuilder &)>> mWriteAccessors; |
241 | }; | 152 | }; |
153 | |||
diff --git a/common/queryrunner.cpp b/common/queryrunner.cpp index 43f48c0..f196965 100644 --- a/common/queryrunner.cpp +++ b/common/queryrunner.cpp | |||
@@ -231,11 +231,11 @@ ReplayResult QueryWorker<DomainType>::executeIncrementalQuery(const Sink::Query | |||
231 | auto replayResult = resultSet.replaySet(0, 0, [this, query, &resultProvider](const ResultSet::Result &result) { | 231 | auto replayResult = resultSet.replaySet(0, 0, [this, query, &resultProvider](const ResultSet::Result &result) { |
232 | resultProviderCallback(query, resultProvider, result); | 232 | resultProviderCallback(query, resultProvider, result); |
233 | }); | 233 | }); |
234 | 234 | preparedQuery.updateComplete(); | |
235 | SinkTraceCtx(mLogCtx) << "Replayed " << replayResult.replayedEntities << " results.\n" | 235 | SinkTraceCtx(mLogCtx) << "Replayed " << replayResult.replayedEntities << " results.\n" |
236 | << (replayResult.replayedAll ? "Replayed all available results.\n" : "") | 236 | << (replayResult.replayedAll ? "Replayed all available results.\n" : "") |
237 | << "Incremental query took: " << Log::TraceTime(time.elapsed()); | 237 | << "Incremental query took: " << Log::TraceTime(time.elapsed()); |
238 | return {entityStore.maxRevision(), replayResult.replayedEntities, replayResult.replayedAll, preparedQuery.getState()}; | 238 | return {entityStore.maxRevision(), replayResult.replayedEntities, false, preparedQuery.getState()}; |
239 | } | 239 | } |
240 | 240 | ||
241 | template <class DomainType> | 241 | template <class DomainType> |
@@ -264,7 +264,7 @@ ReplayResult QueryWorker<DomainType>::executeInitialQuery( | |||
264 | return DataStoreQuery{modifiedQuery, ApplicationDomain::getTypeName<DomainType>(), entityStore}; | 264 | return DataStoreQuery{modifiedQuery, ApplicationDomain::getTypeName<DomainType>(), entityStore}; |
265 | } | 265 | } |
266 | }(); | 266 | }(); |
267 | auto resultSet = preparedQuery.execute();; | 267 | auto resultSet = preparedQuery.execute(); |
268 | 268 | ||
269 | SinkTraceCtx(mLogCtx) << "Filtered set retrieved." << Log::TraceTime(time.elapsed()); | 269 | SinkTraceCtx(mLogCtx) << "Filtered set retrieved." << Log::TraceTime(time.elapsed()); |
270 | auto replayResult = resultSet.replaySet(0, batchsize, [this, query, &resultProvider](const ResultSet::Result &result) { | 270 | auto replayResult = resultSet.replaySet(0, batchsize, [this, query, &resultProvider](const ResultSet::Result &result) { |
diff --git a/common/queryrunner.h b/common/queryrunner.h index 5308eac..11a302f 100644 --- a/common/queryrunner.h +++ b/common/queryrunner.h | |||
@@ -32,8 +32,6 @@ | |||
32 | class QueryRunnerBase : public QObject | 32 | class QueryRunnerBase : public QObject |
33 | { | 33 | { |
34 | Q_OBJECT | 34 | Q_OBJECT |
35 | protected: | ||
36 | SINK_DEBUG_AREA("queryrunner") | ||
37 | public: | 35 | public: |
38 | typedef std::function<void(Sink::ApplicationDomain::ApplicationDomainType &domainObject)> ResultTransformation; | 36 | typedef std::function<void(Sink::ApplicationDomain::ApplicationDomainType &domainObject)> ResultTransformation; |
39 | 37 | ||
diff --git a/common/resourceaccess.cpp b/common/resourceaccess.cpp index ad8cae9..808d892 100644 --- a/common/resourceaccess.cpp +++ b/common/resourceaccess.cpp | |||
@@ -477,7 +477,7 @@ void ResourceAccess::connected() | |||
477 | 477 | ||
478 | { | 478 | { |
479 | flatbuffers::FlatBufferBuilder fbb; | 479 | flatbuffers::FlatBufferBuilder fbb; |
480 | auto name = fbb.CreateString(QString("PID: %1 ResourceAccess: %2").arg(QCoreApplication::applicationPid()).arg(reinterpret_cast<qlonglong>(this)).toLatin1()); | 480 | auto name = fbb.CreateString(QString("PID: %1 ResourceAccess: %2").arg(QCoreApplication::applicationPid()).arg(reinterpret_cast<qlonglong>(this)).toLatin1().toStdString()); |
481 | auto command = Sink::Commands::CreateHandshake(fbb, name); | 481 | auto command = Sink::Commands::CreateHandshake(fbb, name); |
482 | Sink::Commands::FinishHandshakeBuffer(fbb, command); | 482 | Sink::Commands::FinishHandshakeBuffer(fbb, command); |
483 | Commands::write(d->socket.data(), ++d->messageId, Commands::HandshakeCommand, fbb); | 483 | Commands::write(d->socket.data(), ++d->messageId, Commands::HandshakeCommand, fbb); |
@@ -547,6 +547,8 @@ static Sink::Notification getNotification(const Sink::Commands::Notification *bu | |||
547 | } | 547 | } |
548 | n.type = buffer->type(); | 548 | n.type = buffer->type(); |
549 | n.code = buffer->code(); | 549 | n.code = buffer->code(); |
550 | n.progress = buffer->progress(); | ||
551 | n.total = buffer->total(); | ||
550 | n.entities = BufferUtils::fromVector(*buffer->entities()); | 552 | n.entities = BufferUtils::fromVector(*buffer->entities()); |
551 | return n; | 553 | return n; |
552 | } | 554 | } |
diff --git a/common/resourceaccess.h b/common/resourceaccess.h index c32566b..b6a0b34 100644 --- a/common/resourceaccess.h +++ b/common/resourceaccess.h | |||
@@ -101,7 +101,6 @@ protected: | |||
101 | class SINK_EXPORT ResourceAccess : public ResourceAccessInterface | 101 | class SINK_EXPORT ResourceAccess : public ResourceAccessInterface |
102 | { | 102 | { |
103 | Q_OBJECT | 103 | Q_OBJECT |
104 | SINK_DEBUG_AREA("communication") | ||
105 | public: | 104 | public: |
106 | typedef QSharedPointer<ResourceAccess> Ptr; | 105 | typedef QSharedPointer<ResourceAccess> Ptr; |
107 | 106 | ||
@@ -158,7 +157,6 @@ private: | |||
158 | */ | 157 | */ |
159 | class SINK_EXPORT ResourceAccessFactory | 158 | class SINK_EXPORT ResourceAccessFactory |
160 | { | 159 | { |
161 | SINK_DEBUG_AREA("ResourceAccessFactory") | ||
162 | public: | 160 | public: |
163 | static ResourceAccessFactory &instance(); | 161 | static ResourceAccessFactory &instance(); |
164 | Sink::ResourceAccess::Ptr getAccess(const QByteArray &instanceIdentifier, const QByteArray resourceType); | 162 | Sink::ResourceAccess::Ptr getAccess(const QByteArray &instanceIdentifier, const QByteArray resourceType); |
diff --git a/common/resourcecontrol.cpp b/common/resourcecontrol.cpp index 70a3f7d..b6a4c0b 100644 --- a/common/resourcecontrol.cpp +++ b/common/resourcecontrol.cpp | |||
@@ -30,8 +30,6 @@ | |||
30 | #include "log.h" | 30 | #include "log.h" |
31 | #include "notifier.h" | 31 | #include "notifier.h" |
32 | 32 | ||
33 | SINK_DEBUG_AREA("resourcecontrol") | ||
34 | |||
35 | namespace Sink { | 33 | namespace Sink { |
36 | 34 | ||
37 | KAsync::Job<void> ResourceControl::shutdown(const QByteArray &identifier) | 35 | KAsync::Job<void> ResourceControl::shutdown(const QByteArray &identifier) |
diff --git a/common/resourcefacade.cpp b/common/resourcefacade.cpp index dee0711..dab6aed 100644 --- a/common/resourcefacade.cpp +++ b/common/resourcefacade.cpp | |||
@@ -21,16 +21,12 @@ | |||
21 | #include "resourceconfig.h" | 21 | #include "resourceconfig.h" |
22 | #include "query.h" | 22 | #include "query.h" |
23 | #include "definitions.h" | 23 | #include "definitions.h" |
24 | #include "storage.h" | ||
25 | #include "store.h" | 24 | #include "store.h" |
26 | #include "resourceaccess.h" | 25 | #include "resourceaccess.h" |
27 | #include "resource.h" | 26 | #include "resource.h" |
28 | #include <QDir> | ||
29 | 27 | ||
30 | using namespace Sink; | 28 | using namespace Sink; |
31 | 29 | ||
32 | SINK_DEBUG_AREA("ResourceFacade") | ||
33 | |||
34 | template<typename DomainType> | 30 | template<typename DomainType> |
35 | ConfigNotifier LocalStorageFacade<DomainType>::sConfigNotifier; | 31 | ConfigNotifier LocalStorageFacade<DomainType>::sConfigNotifier; |
36 | 32 | ||
@@ -100,17 +96,24 @@ template<typename DomainType> | |||
100 | LocalStorageQueryRunner<DomainType>::LocalStorageQueryRunner(const Query &query, const QByteArray &identifier, const QByteArray &typeName, ConfigNotifier &configNotifier, const Sink::Log::Context &ctx) | 96 | LocalStorageQueryRunner<DomainType>::LocalStorageQueryRunner(const Query &query, const QByteArray &identifier, const QByteArray &typeName, ConfigNotifier &configNotifier, const Sink::Log::Context &ctx) |
101 | : mResultProvider(new ResultProvider<typename DomainType::Ptr>), mConfigStore(identifier, typeName), mGuard(new QObject), mLogCtx(ctx.subContext("config")) | 97 | : mResultProvider(new ResultProvider<typename DomainType::Ptr>), mConfigStore(identifier, typeName), mGuard(new QObject), mLogCtx(ctx.subContext("config")) |
102 | { | 98 | { |
99 | |||
100 | auto matchesTypeAndIds = [query, this] (const QByteArray &type, const QByteArray &id) { | ||
101 | if (query.hasFilter(ApplicationDomain::SinkResource::ResourceType::name) && query.getFilter(ApplicationDomain::SinkResource::ResourceType::name).value.toByteArray() != type) { | ||
102 | SinkTraceCtx(mLogCtx) << "Skipping due to type."; | ||
103 | return false; | ||
104 | } | ||
105 | if (!query.ids().isEmpty() && !query.ids().contains(id)) { | ||
106 | return false; | ||
107 | } | ||
108 | return true; | ||
109 | }; | ||
110 | |||
103 | QObject *guard = new QObject; | 111 | QObject *guard = new QObject; |
104 | mResultProvider->setFetcher([this, query, guard, &configNotifier](const QSharedPointer<DomainType> &) { | 112 | mResultProvider->setFetcher([this, query, guard, &configNotifier, matchesTypeAndIds](const QSharedPointer<DomainType> &) { |
105 | const auto entries = mConfigStore.getEntries(); | 113 | const auto entries = mConfigStore.getEntries(); |
106 | for (const auto &res : entries.keys()) { | 114 | for (const auto &res : entries.keys()) { |
107 | const auto type = entries.value(res); | 115 | const auto type = entries.value(res); |
108 | 116 | if (!matchesTypeAndIds(type, res)){ | |
109 | if (query.hasFilter(ApplicationDomain::SinkResource::ResourceType::name) && query.getFilter(ApplicationDomain::SinkResource::ResourceType::name).value.toByteArray() != type) { | ||
110 | SinkTraceCtx(mLogCtx) << "Skipping due to type."; | ||
111 | continue; | ||
112 | } | ||
113 | if (!query.ids().isEmpty() && !query.ids().contains(res)) { | ||
114 | continue; | 117 | continue; |
115 | } | 118 | } |
116 | auto entity = readFromConfig<DomainType>(mConfigStore, res, type, query.requestedProperties); | 119 | auto entity = readFromConfig<DomainType>(mConfigStore, res, type, query.requestedProperties); |
@@ -128,8 +131,14 @@ LocalStorageQueryRunner<DomainType>::LocalStorageQueryRunner(const Query &query, | |||
128 | }); | 131 | }); |
129 | if (query.liveQuery()) { | 132 | if (query.liveQuery()) { |
130 | { | 133 | { |
131 | auto ret = QObject::connect(&configNotifier, &ConfigNotifier::added, guard, [this](const ApplicationDomain::ApplicationDomainType::Ptr &entry) { | 134 | auto ret = QObject::connect(&configNotifier, &ConfigNotifier::added, guard, [this, query, matchesTypeAndIds](const ApplicationDomain::ApplicationDomainType::Ptr &entry, const QByteArray &type) { |
132 | auto entity = entry.staticCast<DomainType>(); | 135 | auto entity = entry.staticCast<DomainType>(); |
136 | if (!matchesTypeAndIds(type, entity->identifier())){ | ||
137 | return; | ||
138 | } | ||
139 | if (!matchesFilter(query.getBaseFilters(), *entity)){ | ||
140 | return; | ||
141 | } | ||
133 | SinkTraceCtx(mLogCtx) << "A new resource has been added: " << entity->identifier(); | 142 | SinkTraceCtx(mLogCtx) << "A new resource has been added: " << entity->identifier(); |
134 | updateStatus(*entity); | 143 | updateStatus(*entity); |
135 | mResultProvider->add(entity); | 144 | mResultProvider->add(entity); |
@@ -137,8 +146,14 @@ LocalStorageQueryRunner<DomainType>::LocalStorageQueryRunner(const Query &query, | |||
137 | Q_ASSERT(ret); | 146 | Q_ASSERT(ret); |
138 | } | 147 | } |
139 | { | 148 | { |
140 | auto ret = QObject::connect(&configNotifier, &ConfigNotifier::modified, guard, [this](const ApplicationDomain::ApplicationDomainType::Ptr &entry) { | 149 | auto ret = QObject::connect(&configNotifier, &ConfigNotifier::modified, guard, [this, query, matchesTypeAndIds](const ApplicationDomain::ApplicationDomainType::Ptr &entry, const QByteArray &type) { |
141 | auto entity = entry.staticCast<DomainType>(); | 150 | auto entity = entry.staticCast<DomainType>(); |
151 | if (!matchesTypeAndIds(type, entity->identifier())){ | ||
152 | return; | ||
153 | } | ||
154 | if (!matchesFilter(query.getBaseFilters(), *entity)){ | ||
155 | return; | ||
156 | } | ||
142 | updateStatus(*entity); | 157 | updateStatus(*entity); |
143 | mResultProvider->modify(entity); | 158 | mResultProvider->modify(entity); |
144 | }); | 159 | }); |
@@ -222,7 +237,7 @@ KAsync::Job<void> LocalStorageFacade<DomainType>::create(const DomainType &domai | |||
222 | } | 237 | } |
223 | configStore.modify(identifier, configurationValues); | 238 | configStore.modify(identifier, configurationValues); |
224 | } | 239 | } |
225 | sConfigNotifier.add(::readFromConfig<DomainType>(configStore, identifier, type, QByteArrayList{})); | 240 | sConfigNotifier.add(::readFromConfig<DomainType>(configStore, identifier, type, QByteArrayList{}), type); |
226 | }); | 241 | }); |
227 | } | 242 | } |
228 | 243 | ||
@@ -251,7 +266,7 @@ KAsync::Job<void> LocalStorageFacade<DomainType>::modify(const DomainType &domai | |||
251 | } | 266 | } |
252 | 267 | ||
253 | const auto type = configStore.getEntries().value(identifier); | 268 | const auto type = configStore.getEntries().value(identifier); |
254 | sConfigNotifier.modify(::readFromConfig<DomainType>(configStore, identifier, type, QByteArrayList{})); | 269 | sConfigNotifier.modify(::readFromConfig<DomainType>(configStore, identifier, type, QByteArrayList{}), type); |
255 | }); | 270 | }); |
256 | } | 271 | } |
257 | 272 | ||
@@ -281,7 +296,7 @@ KAsync::Job<void> LocalStorageFacade<DomainType>::remove(const DomainType &domai | |||
281 | SinkTrace() << "Removing: " << identifier; | 296 | SinkTrace() << "Removing: " << identifier; |
282 | auto configStore = ConfigStore(configStoreIdentifier, typeName); | 297 | auto configStore = ConfigStore(configStoreIdentifier, typeName); |
283 | configStore.remove(identifier); | 298 | configStore.remove(identifier); |
284 | sConfigNotifier.remove(QSharedPointer<DomainType>::create(domainObject)); | 299 | sConfigNotifier.remove(QSharedPointer<DomainType>::create(domainObject), typeName); |
285 | }); | 300 | }); |
286 | } | 301 | } |
287 | 302 | ||
@@ -373,10 +388,10 @@ QPair<KAsync::Job<void>, typename Sink::ResultEmitter<typename ApplicationDomain | |||
373 | if (states.contains(ApplicationDomain::BusyStatus)) { | 388 | if (states.contains(ApplicationDomain::BusyStatus)) { |
374 | return ApplicationDomain::BusyStatus; | 389 | return ApplicationDomain::BusyStatus; |
375 | } | 390 | } |
376 | if (states.contains(ApplicationDomain::ConnectedStatus)) { | 391 | if (states.contains(ApplicationDomain::OfflineStatus)) { |
377 | return ApplicationDomain::ConnectedStatus; | 392 | return ApplicationDomain::OfflineStatus; |
378 | } | 393 | } |
379 | return ApplicationDomain::OfflineStatus; | 394 | return ApplicationDomain::ConnectedStatus; |
380 | }(); | 395 | }(); |
381 | account.setStatusStatus(status); | 396 | account.setStatusStatus(status); |
382 | }); | 397 | }); |
diff --git a/common/resourcefacade.h b/common/resourcefacade.h index 1cc075c..76fadce 100644 --- a/common/resourcefacade.h +++ b/common/resourcefacade.h | |||
@@ -36,24 +36,24 @@ class ConfigNotifier : public QObject | |||
36 | { | 36 | { |
37 | Q_OBJECT | 37 | Q_OBJECT |
38 | public: | 38 | public: |
39 | void add(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account) | 39 | void add(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account, const QByteArray &type) |
40 | { | 40 | { |
41 | emit added(account); | 41 | emit added(account, type); |
42 | } | 42 | } |
43 | 43 | ||
44 | void remove(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account) | 44 | void remove(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account, const QByteArray &type) |
45 | { | 45 | { |
46 | emit removed(account); | 46 | emit removed(account, type); |
47 | } | 47 | } |
48 | 48 | ||
49 | void modify(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account) | 49 | void modify(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account, const QByteArray &type) |
50 | { | 50 | { |
51 | emit modified(account); | 51 | emit modified(account, type); |
52 | } | 52 | } |
53 | signals: | 53 | signals: |
54 | void added(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account); | 54 | void added(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account, const QByteArray &type); |
55 | void removed(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account); | 55 | void removed(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account, const QByteArray &type); |
56 | void modified(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account); | 56 | void modified(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account, const QByteArray &type); |
57 | }; | 57 | }; |
58 | 58 | ||
59 | template <typename DomainType> | 59 | template <typename DomainType> |
diff --git a/common/specialpurposepreprocessor.cpp b/common/specialpurposepreprocessor.cpp index 25a6d1a..54f7f46 100644 --- a/common/specialpurposepreprocessor.cpp +++ b/common/specialpurposepreprocessor.cpp | |||
@@ -5,8 +5,6 @@ | |||
5 | 5 | ||
6 | using namespace Sink; | 6 | using namespace Sink; |
7 | 7 | ||
8 | SINK_DEBUG_AREA("SpecialPurposeProcessor") | ||
9 | |||
10 | static QHash<QByteArray, QString> specialPurposeFolders() | 8 | static QHash<QByteArray, QString> specialPurposeFolders() |
11 | { | 9 | { |
12 | QHash<QByteArray, QString> hash; | 10 | QHash<QByteArray, QString> hash; |
diff --git a/common/storage.h b/common/storage.h index 71e9401..8c129df 100644 --- a/common/storage.h +++ b/common/storage.h | |||
@@ -25,10 +25,19 @@ | |||
25 | #include <string> | 25 | #include <string> |
26 | #include <functional> | 26 | #include <functional> |
27 | #include <QString> | 27 | #include <QString> |
28 | #include <QMap> | ||
28 | 29 | ||
29 | namespace Sink { | 30 | namespace Sink { |
30 | namespace Storage { | 31 | namespace Storage { |
31 | 32 | ||
33 | struct SINK_EXPORT DbLayout { | ||
34 | typedef QMap<QByteArray, int> Databases; | ||
35 | DbLayout(); | ||
36 | DbLayout(const QByteArray &, const Databases &); | ||
37 | QByteArray name; | ||
38 | Databases tables; | ||
39 | }; | ||
40 | |||
32 | class SINK_EXPORT DataStore | 41 | class SINK_EXPORT DataStore |
33 | { | 42 | { |
34 | public: | 43 | public: |
@@ -132,7 +141,6 @@ public: | |||
132 | void abort(); | 141 | void abort(); |
133 | 142 | ||
134 | QList<QByteArray> getDatabaseNames() const; | 143 | QList<QByteArray> getDatabaseNames() const; |
135 | bool validateNamedDatabases(); | ||
136 | 144 | ||
137 | NamedDatabase openDatabase(const QByteArray &name = {"default"}, | 145 | NamedDatabase openDatabase(const QByteArray &name = {"default"}, |
138 | const std::function<void(const DataStore::Error &error)> &errorHandler = {}, bool allowDuplicates = false) const; | 146 | const std::function<void(const DataStore::Error &error)> &errorHandler = {}, bool allowDuplicates = false) const; |
@@ -152,6 +160,7 @@ public: | |||
152 | }; | 160 | }; |
153 | 161 | ||
154 | DataStore(const QString &storageRoot, const QString &name, AccessMode mode = ReadOnly); | 162 | DataStore(const QString &storageRoot, const QString &name, AccessMode mode = ReadOnly); |
163 | DataStore(const QString &storageRoot, const DbLayout &layout, AccessMode mode = ReadOnly); | ||
155 | ~DataStore(); | 164 | ~DataStore(); |
156 | 165 | ||
157 | Transaction createTransaction(AccessMode mode = ReadWrite, const std::function<void(const DataStore::Error &error)> &errorHandler = std::function<void(const DataStore::Error &error)>()); | 166 | Transaction createTransaction(AccessMode mode = ReadWrite, const std::function<void(const DataStore::Error &error)> &errorHandler = std::function<void(const DataStore::Error &error)>()); |
diff --git a/common/storage/entitystore.cpp b/common/storage/entitystore.cpp index b7309ab..22e5ae3 100644 --- a/common/storage/entitystore.cpp +++ b/common/storage/entitystore.cpp | |||
@@ -36,9 +36,75 @@ | |||
36 | using namespace Sink; | 36 | using namespace Sink; |
37 | using namespace Sink::Storage; | 37 | using namespace Sink::Storage; |
38 | 38 | ||
39 | static QMap<QByteArray, int> baseDbs() | ||
40 | { | ||
41 | return {{"revisionType", 0}, | ||
42 | {"revisions", 0}, | ||
43 | {"uids", 0}, | ||
44 | {"default", 0}, | ||
45 | {"__flagtable", 0}}; | ||
46 | } | ||
47 | |||
48 | template <typename T, typename First> | ||
49 | void mergeImpl(T &map, First f) | ||
50 | { | ||
51 | for (auto it = f.constBegin(); it != f.constEnd(); it++) { | ||
52 | map.insert(it.key(), it.value()); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | template <typename T, typename First, typename ... Tail> | ||
57 | void mergeImpl(T &map, First f, Tail ...maps) | ||
58 | { | ||
59 | for (auto it = f.constBegin(); it != f.constEnd(); it++) { | ||
60 | map.insert(it.key(), it.value()); | ||
61 | } | ||
62 | mergeImpl<T, Tail...>(map, maps...); | ||
63 | } | ||
64 | |||
65 | template <typename First, typename ... Tail> | ||
66 | First merge(First f, Tail ...maps) | ||
67 | { | ||
68 | First map; | ||
69 | mergeImpl(map, f, maps...); | ||
70 | return map; | ||
71 | } | ||
72 | |||
73 | template <class T> | ||
74 | struct DbLayoutHelper { | ||
75 | void operator()(QMap<QByteArray, int> map) const { | ||
76 | mergeImpl(map, ApplicationDomain::TypeImplementation<T>::typeDatabases()); | ||
77 | } | ||
78 | }; | ||
79 | |||
80 | static Sink::Storage::DbLayout dbLayout(const QByteArray &instanceId) | ||
81 | { | ||
82 | static auto databases = [] { | ||
83 | QMap<QByteArray, int> map; | ||
84 | mergeImpl(map, ApplicationDomain::TypeImplementation<ApplicationDomain::Mail>::typeDatabases()); | ||
85 | mergeImpl(map, ApplicationDomain::TypeImplementation<ApplicationDomain::Folder>::typeDatabases()); | ||
86 | mergeImpl(map, ApplicationDomain::TypeImplementation<ApplicationDomain::Contact>::typeDatabases()); | ||
87 | mergeImpl(map, ApplicationDomain::TypeImplementation<ApplicationDomain::Addressbook>::typeDatabases()); | ||
88 | mergeImpl(map, ApplicationDomain::TypeImplementation<ApplicationDomain::Event>::typeDatabases()); | ||
89 | return merge(baseDbs(), map); | ||
90 | }(); | ||
91 | return {instanceId, databases}; | ||
92 | } | ||
93 | |||
94 | |||
39 | class EntityStore::Private { | 95 | class EntityStore::Private { |
40 | public: | 96 | public: |
41 | Private(const ResourceContext &context, const Sink::Log::Context &ctx) : resourceContext(context), logCtx(ctx.subContext("entitystore")) {} | 97 | Private(const ResourceContext &context, const Sink::Log::Context &ctx) : resourceContext(context), logCtx(ctx.subContext("entitystore")) |
98 | { | ||
99 | static bool initialized = false; | ||
100 | if (!initialized) { | ||
101 | if (QDir{}.mkpath(entityBlobStorageDir())) { | ||
102 | initialized = true; | ||
103 | } else { | ||
104 | SinkWarningCtx(logCtx) << "Failed to create the directory: " << entityBlobStorageDir(); | ||
105 | } | ||
106 | } | ||
107 | } | ||
42 | 108 | ||
43 | ResourceContext resourceContext; | 109 | ResourceContext resourceContext; |
44 | DataStore::Transaction transaction; | 110 | DataStore::Transaction transaction; |
@@ -56,7 +122,7 @@ public: | |||
56 | return transaction; | 122 | return transaction; |
57 | } | 123 | } |
58 | 124 | ||
59 | Sink::Storage::DataStore store(Sink::storageLocation(), resourceContext.instanceId(), DataStore::ReadOnly); | 125 | Sink::Storage::DataStore store(Sink::storageLocation(), dbLayout(resourceContext.instanceId()), DataStore::ReadOnly); |
60 | transaction = store.createTransaction(DataStore::ReadOnly); | 126 | transaction = store.createTransaction(DataStore::ReadOnly); |
61 | return transaction; | 127 | return transaction; |
62 | } | 128 | } |
@@ -93,9 +159,14 @@ public: | |||
93 | return ApplicationDomain::ApplicationDomainType{resourceContext.instanceId(), uid, revision, adaptor}; | 159 | return ApplicationDomain::ApplicationDomainType{resourceContext.instanceId(), uid, revision, adaptor}; |
94 | } | 160 | } |
95 | 161 | ||
162 | QString entityBlobStorageDir() | ||
163 | { | ||
164 | return Sink::resourceStorageLocation(resourceContext.instanceId()) + "/blob"; | ||
165 | } | ||
166 | |||
96 | QString entityBlobStoragePath(const QByteArray &id) | 167 | QString entityBlobStoragePath(const QByteArray &id) |
97 | { | 168 | { |
98 | return Sink::resourceStorageLocation(resourceContext.instanceId()) + "/blob/" + id; | 169 | return entityBlobStorageDir() +"/"+ id; |
99 | } | 170 | } |
100 | 171 | ||
101 | }; | 172 | }; |
@@ -110,9 +181,8 @@ void EntityStore::startTransaction(Sink::Storage::DataStore::AccessMode accessMo | |||
110 | { | 181 | { |
111 | SinkTraceCtx(d->logCtx) << "Starting transaction: " << accessMode; | 182 | SinkTraceCtx(d->logCtx) << "Starting transaction: " << accessMode; |
112 | Q_ASSERT(!d->transaction); | 183 | Q_ASSERT(!d->transaction); |
113 | Sink::Storage::DataStore store(Sink::storageLocation(), d->resourceContext.instanceId(), accessMode); | 184 | Sink::Storage::DataStore store(Sink::storageLocation(), dbLayout(d->resourceContext.instanceId()), accessMode); |
114 | d->transaction = store.createTransaction(accessMode); | 185 | d->transaction = store.createTransaction(accessMode); |
115 | Q_ASSERT(d->transaction.validateNamedDatabases()); | ||
116 | } | 186 | } |
117 | 187 | ||
118 | void EntityStore::commitTransaction() | 188 | void EntityStore::commitTransaction() |
@@ -138,9 +208,6 @@ bool EntityStore::hasTransaction() const | |||
138 | void EntityStore::copyBlobs(ApplicationDomain::ApplicationDomainType &entity, qint64 newRevision) | 208 | void EntityStore::copyBlobs(ApplicationDomain::ApplicationDomainType &entity, qint64 newRevision) |
139 | { | 209 | { |
140 | const auto directory = d->entityBlobStoragePath(entity.identifier()); | 210 | const auto directory = d->entityBlobStoragePath(entity.identifier()); |
141 | if (!QDir().mkpath(directory)) { | ||
142 | SinkWarningCtx(d->logCtx) << "Failed to create the directory: " << directory; | ||
143 | } | ||
144 | 211 | ||
145 | for (const auto &property : entity.changedProperties()) { | 212 | for (const auto &property : entity.changedProperties()) { |
146 | const auto value = entity.getProperty(property); | 213 | const auto value = entity.getProperty(property); |
@@ -149,7 +216,7 @@ void EntityStore::copyBlobs(ApplicationDomain::ApplicationDomainType &entity, qi | |||
149 | //Any blob that is not part of the storage yet has to be moved there. | 216 | //Any blob that is not part of the storage yet has to be moved there. |
150 | if (blob.isExternal) { | 217 | if (blob.isExternal) { |
151 | auto oldPath = blob.value; | 218 | auto oldPath = blob.value; |
152 | auto filePath = directory + QString("/%1%2.blob").arg(QString::number(newRevision)).arg(QString::fromLatin1(property)); | 219 | auto filePath = directory + QString("_%1%2.blob").arg(QString::number(newRevision)).arg(QString::fromLatin1(property)); |
153 | //In case we hit the same revision again due to a rollback. | 220 | //In case we hit the same revision again due to a rollback. |
154 | QFile::remove(filePath); | 221 | QFile::remove(filePath); |
155 | QFile origFile(oldPath); | 222 | QFile origFile(oldPath); |
@@ -320,6 +387,11 @@ void EntityStore::cleanupEntityRevisionsUntil(qint64 revision) | |||
320 | { | 387 | { |
321 | const auto uid = DataStore::getUidFromRevision(d->transaction, revision); | 388 | const auto uid = DataStore::getUidFromRevision(d->transaction, revision); |
322 | const auto bufferType = DataStore::getTypeFromRevision(d->transaction, revision); | 389 | const auto bufferType = DataStore::getTypeFromRevision(d->transaction, revision); |
390 | if (bufferType.isEmpty() || uid.isEmpty()) { | ||
391 | SinkErrorCtx(d->logCtx) << "Failed to find revision during cleanup: " << revision; | ||
392 | Q_ASSERT(false); | ||
393 | return; | ||
394 | } | ||
323 | SinkTraceCtx(d->logCtx) << "Cleaning up revision " << revision << uid << bufferType; | 395 | SinkTraceCtx(d->logCtx) << "Cleaning up revision " << revision << uid << bufferType; |
324 | DataStore::mainDatabase(d->transaction, bufferType) | 396 | DataStore::mainDatabase(d->transaction, bufferType) |
325 | .scan(uid, | 397 | .scan(uid, |
@@ -337,10 +409,10 @@ void EntityStore::cleanupEntityRevisionsUntil(qint64 revision) | |||
337 | DataStore::mainDatabase(d->transaction, bufferType).remove(key); | 409 | DataStore::mainDatabase(d->transaction, bufferType).remove(key); |
338 | } | 410 | } |
339 | if (isRemoval) { | 411 | if (isRemoval) { |
340 | const auto directory = d->entityBlobStoragePath(uid); | 412 | QDir dir{d->entityBlobStorageDir()}; |
341 | QDir dir(directory); | 413 | const auto infoList = dir.entryInfoList(QStringList{} << QString{uid + "*"}); |
342 | if (!dir.removeRecursively()) { | 414 | for (const auto &fileInfo : infoList) { |
343 | SinkErrorCtx(d->logCtx) << "Failed to cleanup: " << directory; | 415 | QFile::remove(fileInfo.filePath()); |
344 | } | 416 | } |
345 | } | 417 | } |
346 | //Don't cleanup more than specified | 418 | //Don't cleanup more than specified |
@@ -614,29 +686,6 @@ qint64 EntityStore::maxRevision() | |||
614 | return DataStore::maxRevision(d->getTransaction()); | 686 | return DataStore::maxRevision(d->getTransaction()); |
615 | } | 687 | } |
616 | 688 | ||
617 | /* DataStore::Transaction getTransaction() */ | ||
618 | /* { */ | ||
619 | /* Sink::Storage::DataStore::Transaction transaction; */ | ||
620 | /* { */ | ||
621 | /* Sink::Storage::DataStore storage(Sink::storageLocation(), mResourceInstanceIdentifier); */ | ||
622 | /* if (!storage.exists()) { */ | ||
623 | /* //This is not an error if the resource wasn't started before */ | ||
624 | /* SinkLogCtx(d->logCtx) << "Store doesn't exist: " << mResourceInstanceIdentifier; */ | ||
625 | /* return Sink::Storage::DataStore::Transaction(); */ | ||
626 | /* } */ | ||
627 | /* storage.setDefaultErrorHandler([this](const Sink::Storage::DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Error during query: " << error.store << error.message; }); */ | ||
628 | /* transaction = storage.createTransaction(Sink::Storage::DataStore::ReadOnly); */ | ||
629 | /* } */ | ||
630 | |||
631 | /* //FIXME this is a temporary measure to recover from a failure to open the named databases correctly. */ | ||
632 | /* //Once the actual problem is fixed it will be enough to simply crash if we open the wrong database (which we check in openDatabase already). */ | ||
633 | /* while (!transaction.validateNamedDatabases()) { */ | ||
634 | /* Sink::Storage::DataStore storage(Sink::storageLocation(), mResourceInstanceIdentifier); */ | ||
635 | /* transaction = storage.createTransaction(Sink::Storage::DataStore::ReadOnly); */ | ||
636 | /* } */ | ||
637 | /* return transaction; */ | ||
638 | /* } */ | ||
639 | |||
640 | Sink::Log::Context EntityStore::logContext() const | 689 | Sink::Log::Context EntityStore::logContext() const |
641 | { | 690 | { |
642 | return d->logCtx; | 691 | return d->logCtx; |
diff --git a/common/storage_common.cpp b/common/storage_common.cpp index 81a38c7..8603787 100644 --- a/common/storage_common.cpp +++ b/common/storage_common.cpp | |||
@@ -24,8 +24,6 @@ | |||
24 | #include "log.h" | 24 | #include "log.h" |
25 | #include <QUuid> | 25 | #include <QUuid> |
26 | 26 | ||
27 | SINK_DEBUG_AREA("storage") | ||
28 | |||
29 | QDebug& operator<<(QDebug &dbg, const Sink::Storage::DataStore::Error &error) | 27 | QDebug& operator<<(QDebug &dbg, const Sink::Storage::DataStore::Error &error) |
30 | { | 28 | { |
31 | dbg << error.message << "Code: " << error.code << "Db: " << error.store; | 29 | dbg << error.message << "Code: " << error.code << "Db: " << error.store; |
@@ -38,6 +36,18 @@ namespace Storage { | |||
38 | static const char *s_internalPrefix = "__internal"; | 36 | static const char *s_internalPrefix = "__internal"; |
39 | static const int s_internalPrefixSize = strlen(s_internalPrefix); | 37 | static const int s_internalPrefixSize = strlen(s_internalPrefix); |
40 | 38 | ||
39 | DbLayout::DbLayout() | ||
40 | { | ||
41 | |||
42 | } | ||
43 | |||
44 | DbLayout::DbLayout(const QByteArray &n, const Databases &t) | ||
45 | : name(n), | ||
46 | tables(t) | ||
47 | { | ||
48 | |||
49 | } | ||
50 | |||
41 | void errorHandler(const DataStore::Error &error) | 51 | void errorHandler(const DataStore::Error &error) |
42 | { | 52 | { |
43 | if (error.code == DataStore::TransactionError) { | 53 | if (error.code == DataStore::TransactionError) { |
@@ -113,7 +123,7 @@ QByteArray DataStore::getUidFromRevision(const DataStore::Transaction &transacti | |||
113 | transaction.openDatabase("revisions") | 123 | transaction.openDatabase("revisions") |
114 | .scan(QByteArray::number(revision), | 124 | .scan(QByteArray::number(revision), |
115 | [&](const QByteArray &, const QByteArray &value) -> bool { | 125 | [&](const QByteArray &, const QByteArray &value) -> bool { |
116 | uid = value; | 126 | uid = QByteArray{value.constData(), value.size()}; |
117 | return false; | 127 | return false; |
118 | }, | 128 | }, |
119 | [revision](const Error &error) { SinkWarning() << "Couldn't find uid for revision: " << revision << error.message; }); | 129 | [revision](const Error &error) { SinkWarning() << "Couldn't find uid for revision: " << revision << error.message; }); |
@@ -126,7 +136,7 @@ QByteArray DataStore::getTypeFromRevision(const DataStore::Transaction &transact | |||
126 | transaction.openDatabase("revisionType") | 136 | transaction.openDatabase("revisionType") |
127 | .scan(QByteArray::number(revision), | 137 | .scan(QByteArray::number(revision), |
128 | [&](const QByteArray &, const QByteArray &value) -> bool { | 138 | [&](const QByteArray &, const QByteArray &value) -> bool { |
129 | type = value; | 139 | type = QByteArray{value.constData(), value.size()}; |
130 | return false; | 140 | return false; |
131 | }, | 141 | }, |
132 | [revision](const Error &error) { SinkWarning() << "Couldn't find type for revision " << revision; }); | 142 | [revision](const Error &error) { SinkWarning() << "Couldn't find type for revision " << revision; }); |
@@ -207,6 +217,11 @@ QByteArray DataStore::generateUid() | |||
207 | 217 | ||
208 | DataStore::NamedDatabase DataStore::mainDatabase(const DataStore::Transaction &t, const QByteArray &type) | 218 | DataStore::NamedDatabase DataStore::mainDatabase(const DataStore::Transaction &t, const QByteArray &type) |
209 | { | 219 | { |
220 | if (type.isEmpty()) { | ||
221 | SinkError() << "Tried to open main database for empty type."; | ||
222 | Q_ASSERT(false); | ||
223 | return {}; | ||
224 | } | ||
210 | return t.openDatabase(type + ".main"); | 225 | return t.openDatabase(type + ".main"); |
211 | } | 226 | } |
212 | 227 | ||
diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp index 08eea37..f7999d1 100644 --- a/common/storage_lmdb.cpp +++ b/common/storage_lmdb.cpp | |||
@@ -35,9 +35,6 @@ | |||
35 | #include <lmdb.h> | 35 | #include <lmdb.h> |
36 | #include "log.h" | 36 | #include "log.h" |
37 | 37 | ||
38 | SINK_DEBUG_AREA("storage") | ||
39 | // SINK_DEBUG_COMPONENT(d->storageRoot.toLatin1() + '/' + d->name.toLatin1()) | ||
40 | |||
41 | namespace Sink { | 38 | namespace Sink { |
42 | namespace Storage { | 39 | namespace Storage { |
43 | 40 | ||
@@ -169,6 +166,27 @@ public: | |||
169 | if (const int rc = mdb_dbi_open(transaction, db.constData(), flags, &dbi)) { | 166 | if (const int rc = mdb_dbi_open(transaction, db.constData(), flags, &dbi)) { |
170 | //Create the db if it is not existing already | 167 | //Create the db if it is not existing already |
171 | if (rc == MDB_NOTFOUND && !readOnly) { | 168 | if (rc == MDB_NOTFOUND && !readOnly) { |
169 | //Sanity check db name | ||
170 | { | ||
171 | auto parts = db.split('.'); | ||
172 | for (const auto &p : parts) { | ||
173 | auto containsSpecialCharacter = [] (const QByteArray &p) { | ||
174 | for (int i = 0; i < p.size(); i++) { | ||
175 | const auto c = p.at(i); | ||
176 | //Between 0 and z in the ascii table. Essentially ensures that the name is printable and doesn't contain special chars | ||
177 | if (c < 0x30 || c > 0x7A) { | ||
178 | return true; | ||
179 | } | ||
180 | } | ||
181 | return false; | ||
182 | }; | ||
183 | if (p.isEmpty() || containsSpecialCharacter(p)) { | ||
184 | SinkError() << "Tried to create a db with an invalid name. Hex:" << db.toHex() << " ASCII:" << db; | ||
185 | Q_ASSERT(false); | ||
186 | throw std::runtime_error("Fatal error while creating db."); | ||
187 | } | ||
188 | } | ||
189 | } | ||
172 | if (const int rc = mdb_dbi_open(transaction, db.constData(), flags | MDB_CREATE, &dbi)) { | 190 | if (const int rc = mdb_dbi_open(transaction, db.constData(), flags | MDB_CREATE, &dbi)) { |
173 | SinkWarning() << "Failed to create db " << QByteArray(mdb_strerror(rc)); | 191 | SinkWarning() << "Failed to create db " << QByteArray(mdb_strerror(rc)); |
174 | Error error(name.toLatin1(), ErrorCodes::GenericError, "Error while creating database: " + QByteArray(mdb_strerror(rc))); | 192 | Error error(name.toLatin1(), ErrorCodes::GenericError, "Error while creating database: " + QByteArray(mdb_strerror(rc))); |
@@ -542,6 +560,7 @@ DataStore::Transaction::Transaction(Transaction &&other) : d(nullptr) | |||
542 | DataStore::Transaction &DataStore::Transaction::operator=(DataStore::Transaction &&other) | 560 | DataStore::Transaction &DataStore::Transaction::operator=(DataStore::Transaction &&other) |
543 | { | 561 | { |
544 | if (&other != this) { | 562 | if (&other != this) { |
563 | abort(); | ||
545 | delete d; | 564 | delete d; |
546 | d = other.d; | 565 | d = other.d; |
547 | other.d = nullptr; | 566 | other.d = nullptr; |
@@ -639,11 +658,6 @@ static bool ensureCorrectDb(DataStore::NamedDatabase &database, const QByteArray | |||
639 | return !openedTheWrongDatabase; | 658 | return !openedTheWrongDatabase; |
640 | } | 659 | } |
641 | 660 | ||
642 | bool DataStore::Transaction::validateNamedDatabases() | ||
643 | { | ||
644 | return true; | ||
645 | } | ||
646 | |||
647 | DataStore::NamedDatabase DataStore::Transaction::openDatabase(const QByteArray &db, const std::function<void(const DataStore::Error &error)> &errorHandler, bool allowDuplicates) const | 661 | DataStore::NamedDatabase DataStore::Transaction::openDatabase(const QByteArray &db, const std::function<void(const DataStore::Error &error)> &errorHandler, bool allowDuplicates) const |
648 | { | 662 | { |
649 | if (!d) { | 663 | if (!d) { |
@@ -694,7 +708,7 @@ QList<QByteArray> DataStore::Transaction::getDatabaseNames() const | |||
694 | class DataStore::Private | 708 | class DataStore::Private |
695 | { | 709 | { |
696 | public: | 710 | public: |
697 | Private(const QString &s, const QString &n, AccessMode m); | 711 | Private(const QString &s, const QString &n, AccessMode m, const DbLayout &layout = {}); |
698 | ~Private(); | 712 | ~Private(); |
699 | 713 | ||
700 | QString storageRoot; | 714 | QString storageRoot; |
@@ -702,68 +716,85 @@ public: | |||
702 | 716 | ||
703 | MDB_env *env; | 717 | MDB_env *env; |
704 | AccessMode mode; | 718 | AccessMode mode; |
719 | Sink::Log::Context logCtx; | ||
720 | |||
721 | void initEnvironment(const QString &fullPath, const DbLayout &layout) | ||
722 | { | ||
723 | // Ensure the environment is only created once, and that we only have one environment per process | ||
724 | if (!(env = sEnvironments.value(fullPath))) { | ||
725 | QMutexLocker locker(&sMutex); | ||
726 | if (!(env = sEnvironments.value(fullPath))) { | ||
727 | int rc = 0; | ||
728 | if ((rc = mdb_env_create(&env))) { | ||
729 | SinkWarningCtx(logCtx) << "mdb_env_create: " << rc << " " << mdb_strerror(rc); | ||
730 | qCritical() << "mdb_env_create: " << rc << " " << mdb_strerror(rc); | ||
731 | } else { | ||
732 | //Limit large enough to accomodate all our named dbs. This only starts to matter if the number gets large, otherwise it's just a bunch of extra entries in the main table. | ||
733 | mdb_env_set_maxdbs(env, 50); | ||
734 | const bool readOnly = (mode == ReadOnly); | ||
735 | unsigned int flags = MDB_NOTLS; | ||
736 | if (readOnly) { | ||
737 | flags |= MDB_RDONLY; | ||
738 | } | ||
739 | if ((rc = mdb_env_open(env, fullPath.toStdString().data(), flags, 0664))) { | ||
740 | SinkWarningCtx(logCtx) << "mdb_env_open: " << rc << ":" << mdb_strerror(rc); | ||
741 | mdb_env_close(env); | ||
742 | env = 0; | ||
743 | } else { | ||
744 | if (RUNNING_ON_VALGRIND) { | ||
745 | // In order to run valgrind this size must be smaller than half your available RAM | ||
746 | // https://github.com/BVLC/caffe/issues/2404 | ||
747 | mdb_env_set_mapsize(env, (size_t)10485760 * (size_t)1000); // 1MB * 1000 | ||
748 | } else { | ||
749 | //This is the maximum size of the db (but will not be used directly), so we make it large enough that we hopefully never run into the limit. | ||
750 | mdb_env_set_mapsize(env, (size_t)10485760 * (size_t)100000); // 1MB * 1000 | ||
751 | } | ||
752 | Q_ASSERT(env); | ||
753 | sEnvironments.insert(fullPath, env); | ||
754 | //Open all available dbi's | ||
755 | bool noLock = true; | ||
756 | auto t = Transaction(new Transaction::Private(readOnly, nullptr, name, env, noLock)); | ||
757 | if (!layout.tables.isEmpty()) { | ||
758 | |||
759 | //TODO upgrade db if the layout has changed: | ||
760 | //* read existing layout | ||
761 | //* if layout is not the same create new layout | ||
762 | //If the db is read only, abort if the db is not yet existing. | ||
763 | //If the db is not read-only but is not existing, ensure we have a layout and create all tables. | ||
764 | |||
765 | for (auto it = layout.tables.constBegin(); it != layout.tables.constEnd(); it++) { | ||
766 | bool allowDuplicates = it.value(); | ||
767 | t.openDatabase(it.key(), {}, allowDuplicates); | ||
768 | } | ||
769 | } else { | ||
770 | for (const auto &db : t.getDatabaseNames()) { | ||
771 | //Get dbi to store for future use. | ||
772 | t.openDatabase(db); | ||
773 | } | ||
774 | } | ||
775 | //To persist the dbis (this is also necessary for read-only transactions) | ||
776 | t.commit(); | ||
777 | } | ||
778 | } | ||
779 | } | ||
780 | } | ||
781 | } | ||
782 | |||
705 | }; | 783 | }; |
706 | 784 | ||
707 | DataStore::Private::Private(const QString &s, const QString &n, AccessMode m) : storageRoot(s), name(n), env(0), mode(m) | 785 | DataStore::Private::Private(const QString &s, const QString &n, AccessMode m, const DbLayout &layout) : storageRoot(s), name(n), env(0), mode(m), logCtx(n.toLatin1()) |
708 | { | 786 | { |
787 | |||
709 | const QString fullPath(storageRoot + '/' + name); | 788 | const QString fullPath(storageRoot + '/' + name); |
710 | QFileInfo dirInfo(fullPath); | 789 | QFileInfo dirInfo(fullPath); |
711 | if (!dirInfo.exists() && mode == ReadWrite) { | 790 | if (!dirInfo.exists() && mode == ReadWrite) { |
712 | QDir().mkpath(fullPath); | 791 | QDir().mkpath(fullPath); |
713 | dirInfo.refresh(); | 792 | dirInfo.refresh(); |
714 | } | 793 | } |
715 | Sink::Log::Context logCtx{n.toLatin1()}; | ||
716 | if (mode == ReadWrite && !dirInfo.permission(QFile::WriteOwner)) { | 794 | if (mode == ReadWrite && !dirInfo.permission(QFile::WriteOwner)) { |
717 | qCritical() << fullPath << "does not have write permissions. Aborting"; | 795 | qCritical() << fullPath << "does not have write permissions. Aborting"; |
718 | } else if (dirInfo.exists()) { | 796 | } else if (dirInfo.exists()) { |
719 | // Ensure the environment is only created once | 797 | initEnvironment(fullPath, layout); |
720 | QMutexLocker locker(&sMutex); | ||
721 | |||
722 | /* | ||
723 | * It seems we can only ever have one environment open in the process. | ||
724 | * Otherwise multi-threading breaks. | ||
725 | */ | ||
726 | env = sEnvironments.value(fullPath); | ||
727 | if (!env) { | ||
728 | int rc = 0; | ||
729 | if ((rc = mdb_env_create(&env))) { | ||
730 | // TODO: handle error | ||
731 | SinkWarningCtx(logCtx) << "mdb_env_create: " << rc << " " << mdb_strerror(rc); | ||
732 | } else { | ||
733 | mdb_env_set_maxdbs(env, 50); | ||
734 | unsigned int flags = MDB_NOTLS; | ||
735 | if (mode == ReadOnly) { | ||
736 | flags |= MDB_RDONLY; | ||
737 | } | ||
738 | if ((rc = mdb_env_open(env, fullPath.toStdString().data(), flags, 0664))) { | ||
739 | SinkWarningCtx(logCtx) << "mdb_env_open: " << rc << ":" << mdb_strerror(rc); | ||
740 | mdb_env_close(env); | ||
741 | env = 0; | ||
742 | } else { | ||
743 | if (RUNNING_ON_VALGRIND) { | ||
744 | // In order to run valgrind this size must be smaller than half your available RAM | ||
745 | // https://github.com/BVLC/caffe/issues/2404 | ||
746 | const size_t dbSize = (size_t)10485760 * (size_t)1000; // 1MB * 1000 | ||
747 | mdb_env_set_mapsize(env, dbSize); | ||
748 | } else { | ||
749 | // FIXME: dynamic resize | ||
750 | const size_t dbSize = (size_t)10485760 * (size_t)8000; // 1MB * 8000 | ||
751 | mdb_env_set_mapsize(env, dbSize); | ||
752 | } | ||
753 | sEnvironments.insert(fullPath, env); | ||
754 | //Open all available dbi's | ||
755 | bool noLock = true; | ||
756 | bool requestedRead = m == ReadOnly; | ||
757 | auto t = Transaction(new Transaction::Private(requestedRead, nullptr, name, env, noLock)); | ||
758 | for (const auto &db : t.getDatabaseNames()) { | ||
759 | //Get dbi to store for future use. | ||
760 | t.openDatabase(db); | ||
761 | } | ||
762 | //To persist the dbis (this is also necessary for read-only transactions) | ||
763 | t.commit(); | ||
764 | } | ||
765 | } | ||
766 | } | ||
767 | } | 798 | } |
768 | } | 799 | } |
769 | 800 | ||
@@ -777,6 +808,10 @@ DataStore::DataStore(const QString &storageRoot, const QString &name, AccessMode | |||
777 | { | 808 | { |
778 | } | 809 | } |
779 | 810 | ||
811 | DataStore::DataStore(const QString &storageRoot, const DbLayout &dbLayout, AccessMode mode) : d(new Private(storageRoot, dbLayout.name, mode, dbLayout)) | ||
812 | { | ||
813 | } | ||
814 | |||
780 | DataStore::~DataStore() | 815 | DataStore::~DataStore() |
781 | { | 816 | { |
782 | delete d; | 817 | delete d; |
diff --git a/common/store.cpp b/common/store.cpp index d266098..4735113 100644 --- a/common/store.cpp +++ b/common/store.cpp | |||
@@ -36,8 +36,6 @@ | |||
36 | #include "storage.h" | 36 | #include "storage.h" |
37 | #include "log.h" | 37 | #include "log.h" |
38 | 38 | ||
39 | SINK_DEBUG_AREA("store") | ||
40 | |||
41 | Q_DECLARE_METATYPE(QSharedPointer<Sink::ResultEmitter<Sink::ApplicationDomain::SinkResource::Ptr>>) | 39 | Q_DECLARE_METATYPE(QSharedPointer<Sink::ResultEmitter<Sink::ApplicationDomain::SinkResource::Ptr>>) |
42 | Q_DECLARE_METATYPE(QSharedPointer<Sink::ResourceAccessInterface>); | 40 | Q_DECLARE_METATYPE(QSharedPointer<Sink::ResourceAccessInterface>); |
43 | Q_DECLARE_METATYPE(std::shared_ptr<void>); | 41 | Q_DECLARE_METATYPE(std::shared_ptr<void>); |
@@ -277,6 +275,19 @@ KAsync::Job<void> Store::removeDataFromDisk(const QByteArray &identifier) | |||
277 | }); | 275 | }); |
278 | } | 276 | } |
279 | 277 | ||
278 | KAsync::Job<void> Store::upgrade() | ||
279 | { | ||
280 | SinkLog() << "Upgrading..."; | ||
281 | return fetchAll<ApplicationDomain::SinkResource>({}) | ||
282 | .template each([](const ApplicationDomain::SinkResource::Ptr &resource) -> KAsync::Job<void> { | ||
283 | SinkLog() << "Removing caches for " << resource->identifier(); | ||
284 | return removeDataFromDisk(resource->identifier()); | ||
285 | }) | ||
286 | .then([] { | ||
287 | SinkLog() << "Upgrade complete."; | ||
288 | }); | ||
289 | } | ||
290 | |||
280 | static KAsync::Job<void> synchronize(const QByteArray &resource, const Sink::SyncScope &scope) | 291 | static KAsync::Job<void> synchronize(const QByteArray &resource, const Sink::SyncScope &scope) |
281 | { | 292 | { |
282 | SinkLog() << "Synchronizing " << resource << scope; | 293 | SinkLog() << "Synchronizing " << resource << scope; |
diff --git a/common/store.h b/common/store.h index fae76e5..34e14df 100644 --- a/common/store.h +++ b/common/store.h | |||
@@ -122,6 +122,18 @@ KAsync::Job<void> SINK_EXPORT synchronize(const Sink::SyncScope &query); | |||
122 | */ | 122 | */ |
123 | KAsync::Job<void> SINK_EXPORT removeDataFromDisk(const QByteArray &resourceIdentifier); | 123 | KAsync::Job<void> SINK_EXPORT removeDataFromDisk(const QByteArray &resourceIdentifier); |
124 | 124 | ||
125 | /** | ||
126 | * Run upgrade jobs. | ||
127 | * | ||
128 | * Run this to upgrade your local database to a new version. | ||
129 | * Note that this may: | ||
130 | * * take a while | ||
131 | * * remove some/all of your local caches | ||
132 | * | ||
133 | * Note: The initial implementation simply calls removeDataFromDisk for all resources. | ||
134 | */ | ||
135 | KAsync::Job<void> SINK_EXPORT upgrade(); | ||
136 | |||
125 | template <class DomainType> | 137 | template <class DomainType> |
126 | KAsync::Job<DomainType> SINK_EXPORT fetchOne(const Sink::Query &query); | 138 | KAsync::Job<DomainType> SINK_EXPORT fetchOne(const Sink::Query &query); |
127 | 139 | ||
diff --git a/common/synchronizer.cpp b/common/synchronizer.cpp index 3e7bd30..3b32e68 100644 --- a/common/synchronizer.cpp +++ b/common/synchronizer.cpp | |||
@@ -304,9 +304,29 @@ void Synchronizer::emitNotification(Notification::NoticationType type, int code, | |||
304 | emit notify(n); | 304 | emit notify(n); |
305 | } | 305 | } |
306 | 306 | ||
307 | void Synchronizer::reportProgress(int progress, int total) | 307 | void Synchronizer::emitProgressNotification(Notification::NoticationType type, int progress, int total, const QByteArray &id, const QByteArrayList &entities) |
308 | { | 308 | { |
309 | SinkLogCtx(mLogCtx) << "Progress: " << progress << " out of " << total; | 309 | Sink::Notification n; |
310 | n.id = id; | ||
311 | n.type = type; | ||
312 | n.progress = progress; | ||
313 | n.total = total; | ||
314 | n.entities = entities; | ||
315 | emit notify(n); | ||
316 | } | ||
317 | |||
318 | void Synchronizer::reportProgress(int progress, int total, const QByteArrayList &entities) | ||
319 | { | ||
320 | if (progress > 0 && total > 0) { | ||
321 | SinkLogCtx(mLogCtx) << "Progress: " << progress << " out of " << total << mCurrentRequest.requestId << mCurrentRequest.applicableEntities; | ||
322 | const auto applicableEntities = [&] { | ||
323 | if (entities.isEmpty()) { | ||
324 | return mCurrentRequest.applicableEntities; | ||
325 | } | ||
326 | return entities; | ||
327 | }(); | ||
328 | emitProgressNotification(Notification::Progress, progress, total, mCurrentRequest.requestId, applicableEntities); | ||
329 | } | ||
310 | } | 330 | } |
311 | 331 | ||
312 | void Synchronizer::setStatusFromResult(const KAsync::Error &error, const QString &s, const QByteArray &requestId) | 332 | void Synchronizer::setStatusFromResult(const KAsync::Error &error, const QString &s, const QByteArray &requestId) |
@@ -315,6 +335,9 @@ void Synchronizer::setStatusFromResult(const KAsync::Error &error, const QString | |||
315 | if (error.errorCode == ApplicationDomain::ConnectionError) { | 335 | if (error.errorCode == ApplicationDomain::ConnectionError) { |
316 | //Couldn't connect, so we assume we don't have a network connection. | 336 | //Couldn't connect, so we assume we don't have a network connection. |
317 | setStatus(ApplicationDomain::OfflineStatus, s, requestId); | 337 | setStatus(ApplicationDomain::OfflineStatus, s, requestId); |
338 | } else if (error.errorCode == ApplicationDomain::NoServerError) { | ||
339 | //Failed to contact the server. | ||
340 | setStatus(ApplicationDomain::OfflineStatus, s, requestId); | ||
318 | } else if (error.errorCode == ApplicationDomain::ConfigurationError) { | 341 | } else if (error.errorCode == ApplicationDomain::ConfigurationError) { |
319 | //There is an error with the configuration. | 342 | //There is an error with the configuration. |
320 | setStatus(ApplicationDomain::ErrorStatus, s, requestId); | 343 | setStatus(ApplicationDomain::ErrorStatus, s, requestId); |
@@ -354,6 +377,7 @@ KAsync::Job<void> Synchronizer::processRequest(const SyncRequest &request) | |||
354 | } else if (request.requestType == Synchronizer::SyncRequest::Synchronization) { | 377 | } else if (request.requestType == Synchronizer::SyncRequest::Synchronization) { |
355 | return KAsync::start([this, request] { | 378 | return KAsync::start([this, request] { |
356 | SinkLogCtx(mLogCtx) << "Synchronizing: " << request.query; | 379 | SinkLogCtx(mLogCtx) << "Synchronizing: " << request.query; |
380 | setBusy(true, "Synchronization has started.", request.requestId); | ||
357 | emitNotification(Notification::Info, ApplicationDomain::SyncInProgress, {}, {}, request.applicableEntities); | 381 | emitNotification(Notification::Info, ApplicationDomain::SyncInProgress, {}, {}, request.applicableEntities); |
358 | }).then(synchronizeWithSource(request.query)).then([this] { | 382 | }).then(synchronizeWithSource(request.query)).then([this] { |
359 | //Commit after every request, so implementations only have to commit more if they add a lot of data. | 383 | //Commit after every request, so implementations only have to commit more if they add a lot of data. |
@@ -391,11 +415,12 @@ KAsync::Job<void> Synchronizer::processRequest(const SyncRequest &request) | |||
391 | return KAsync::null(); | 415 | return KAsync::null(); |
392 | } else { | 416 | } else { |
393 | return KAsync::start([this, request] { | 417 | return KAsync::start([this, request] { |
418 | setBusy(true, "ChangeReplay has started.", request.requestId); | ||
394 | SinkLogCtx(mLogCtx) << "Replaying changes."; | 419 | SinkLogCtx(mLogCtx) << "Replaying changes."; |
395 | }) | 420 | }) |
396 | .then(replayNextRevision()) | 421 | .then(replayNextRevision()) |
397 | .then<void>([this, request](const KAsync::Error &error) { | 422 | .then<void>([this, request](const KAsync::Error &error) { |
398 | setStatusFromResult(error, "Changereplay has ended.", "changereplay"); | 423 | setStatusFromResult(error, "Changereplay has ended.", request.requestId); |
399 | if (error) { | 424 | if (error) { |
400 | SinkWarningCtx(mLogCtx) << "Changereplay failed: " << error.errorMessage; | 425 | SinkWarningCtx(mLogCtx) << "Changereplay failed: " << error.errorMessage; |
401 | return KAsync::error(error); | 426 | return KAsync::error(error); |
@@ -462,16 +487,13 @@ KAsync::Job<void> Synchronizer::processSyncQueue() | |||
462 | mMessageQueue->startTransaction(); | 487 | mMessageQueue->startTransaction(); |
463 | mEntityStore->startTransaction(Sink::Storage::DataStore::ReadOnly); | 488 | mEntityStore->startTransaction(Sink::Storage::DataStore::ReadOnly); |
464 | mSyncInProgress = true; | 489 | mSyncInProgress = true; |
465 | if (request.requestType == Synchronizer::SyncRequest::Synchronization) { | 490 | mCurrentRequest = request; |
466 | setBusy(true, "Synchronization has started.", request.requestId); | ||
467 | } else if (request.requestType == Synchronizer::SyncRequest::ChangeReplay) { | ||
468 | setBusy(true, "ChangeReplay has started.", "changereplay"); | ||
469 | } | ||
470 | }) | 491 | }) |
471 | .then(processRequest(request)) | 492 | .then(processRequest(request)) |
472 | .then<void>([this, request](const KAsync::Error &error) { | 493 | .then<void>([this, request](const KAsync::Error &error) { |
473 | SinkTraceCtx(mLogCtx) << "Sync request processed"; | 494 | SinkTraceCtx(mLogCtx) << "Sync request processed"; |
474 | setBusy(false, {}, request.requestId); | 495 | setBusy(false, {}, request.requestId); |
496 | mCurrentRequest = {}; | ||
475 | mEntityStore->abortTransaction(); | 497 | mEntityStore->abortTransaction(); |
476 | mSyncTransaction.abort(); | 498 | mSyncTransaction.abort(); |
477 | mMessageQueue->commit(); | 499 | mMessageQueue->commit(); |
@@ -516,7 +538,7 @@ void Synchronizer::revisionChanged() | |||
516 | return; | 538 | return; |
517 | } | 539 | } |
518 | } | 540 | } |
519 | mSyncRequestQueue << Synchronizer::SyncRequest{Synchronizer::SyncRequest::ChangeReplay}; | 541 | mSyncRequestQueue << Synchronizer::SyncRequest{Synchronizer::SyncRequest::ChangeReplay, "changereplay"}; |
520 | processSyncQueue().exec(); | 542 | processSyncQueue().exec(); |
521 | } | 543 | } |
522 | 544 | ||
@@ -607,11 +629,14 @@ KAsync::Job<void> Synchronizer::replay(const QByteArray &type, const QByteArray | |||
607 | } | 629 | } |
608 | }) | 630 | }) |
609 | .then([this](const KAsync::Error &error) { | 631 | .then([this](const KAsync::Error &error) { |
632 | //We need to commit here otherwise the next change-replay step will abort the transaction | ||
633 | mSyncStore.clear(); | ||
634 | mSyncTransaction.commit(); | ||
610 | if (error) { | 635 | if (error) { |
611 | SinkWarningCtx(mLogCtx) << "Failed to replay change: " << error.errorMessage; | 636 | SinkWarningCtx(mLogCtx) << "Failed to replay change: " << error.errorMessage; |
637 | return KAsync::error(error); | ||
612 | } | 638 | } |
613 | mSyncStore.clear(); | 639 | return KAsync::null(); |
614 | mSyncTransaction.commit(); | ||
615 | }); | 640 | }); |
616 | } | 641 | } |
617 | 642 | ||
diff --git a/common/synchronizer.h b/common/synchronizer.h index b1ee122..cc082be 100644 --- a/common/synchronizer.h +++ b/common/synchronizer.h | |||
@@ -131,6 +131,8 @@ protected: | |||
131 | RequestFlush | 131 | RequestFlush |
132 | }; | 132 | }; |
133 | 133 | ||
134 | SyncRequest() = default; | ||
135 | |||
134 | SyncRequest(const Sink::QueryBase &q, const QByteArray &requestId_ = QByteArray(), RequestOptions o = NoOptions) | 136 | SyncRequest(const Sink::QueryBase &q, const QByteArray &requestId_ = QByteArray(), RequestOptions o = NoOptions) |
135 | : requestId(requestId_), | 137 | : requestId(requestId_), |
136 | requestType(Synchronization), | 138 | requestType(Synchronization), |
@@ -145,6 +147,12 @@ protected: | |||
145 | { | 147 | { |
146 | } | 148 | } |
147 | 149 | ||
150 | SyncRequest(RequestType type, const QByteArray &requestId_) | ||
151 | : requestId(requestId_), | ||
152 | requestType(type) | ||
153 | { | ||
154 | } | ||
155 | |||
148 | SyncRequest(RequestType type, int flushType_, const QByteArray &requestId_) | 156 | SyncRequest(RequestType type, int flushType_, const QByteArray &requestId_) |
149 | : flushType(flushType_), | 157 | : flushType(flushType_), |
150 | requestId(requestId_), | 158 | requestId(requestId_), |
@@ -184,11 +192,12 @@ protected: | |||
184 | virtual void mergeIntoQueue(const Synchronizer::SyncRequest &request, QList<Synchronizer::SyncRequest> &queue); | 192 | virtual void mergeIntoQueue(const Synchronizer::SyncRequest &request, QList<Synchronizer::SyncRequest> &queue); |
185 | 193 | ||
186 | void emitNotification(Notification::NoticationType type, int code, const QString &message, const QByteArray &id = QByteArray{}, const QByteArrayList &entiteis = QByteArrayList{}); | 194 | void emitNotification(Notification::NoticationType type, int code, const QString &message, const QByteArray &id = QByteArray{}, const QByteArrayList &entiteis = QByteArrayList{}); |
195 | void emitProgressNotification(Notification::NoticationType type, int progress, int total, const QByteArray &id, const QByteArrayList &entities); | ||
187 | 196 | ||
188 | /** | 197 | /** |
189 | * Report progress for current task | 198 | * Report progress for current task |
190 | */ | 199 | */ |
191 | void reportProgress(int progress, int total); | 200 | virtual void reportProgress(int progress, int total, const QByteArrayList &entities = {}) Q_DECL_OVERRIDE; |
192 | 201 | ||
193 | protected: | 202 | protected: |
194 | Sink::Log::Context mLogCtx; | 203 | Sink::Log::Context mLogCtx; |
@@ -211,6 +220,7 @@ private: | |||
211 | Sink::Storage::DataStore::Transaction mSyncTransaction; | 220 | Sink::Storage::DataStore::Transaction mSyncTransaction; |
212 | std::function<void(int commandId, const QByteArray &data)> mEnqueue; | 221 | std::function<void(int commandId, const QByteArray &data)> mEnqueue; |
213 | QList<SyncRequest> mSyncRequestQueue; | 222 | QList<SyncRequest> mSyncRequestQueue; |
223 | SyncRequest mCurrentRequest; | ||
214 | MessageQueue *mMessageQueue; | 224 | MessageQueue *mMessageQueue; |
215 | bool mSyncInProgress; | 225 | bool mSyncInProgress; |
216 | QMultiHash<QByteArray, SyncRequest> mPendingSyncRequests; | 226 | QMultiHash<QByteArray, SyncRequest> mPendingSyncRequests; |
diff --git a/common/synchronizerstore.cpp b/common/synchronizerstore.cpp index 5364094..79cd920 100644 --- a/common/synchronizerstore.cpp +++ b/common/synchronizerstore.cpp | |||
@@ -25,8 +25,6 @@ | |||
25 | 25 | ||
26 | using namespace Sink; | 26 | using namespace Sink; |
27 | 27 | ||
28 | SINK_DEBUG_AREA("synchronizerstore") | ||
29 | |||
30 | SynchronizerStore::SynchronizerStore(Sink::Storage::DataStore::Transaction &transaction) | 28 | SynchronizerStore::SynchronizerStore(Sink::Storage::DataStore::Transaction &transaction) |
31 | : mTransaction(transaction) | 29 | : mTransaction(transaction) |
32 | { | 30 | { |
diff --git a/common/test.cpp b/common/test.cpp index 90586ba..237d3bb 100644 --- a/common/test.cpp +++ b/common/test.cpp | |||
@@ -29,8 +29,6 @@ | |||
29 | #include "resourceconfig.h" | 29 | #include "resourceconfig.h" |
30 | #include "definitions.h" | 30 | #include "definitions.h" |
31 | 31 | ||
32 | SINK_DEBUG_AREA("test") | ||
33 | |||
34 | using namespace Sink; | 32 | using namespace Sink; |
35 | 33 | ||
36 | void Sink::Test::initTest() | 34 | void Sink::Test::initTest() |
@@ -83,6 +81,7 @@ void Sink::Test::initTest() | |||
83 | void Sink::Test::setTestModeEnabled(bool enabled) | 81 | void Sink::Test::setTestModeEnabled(bool enabled) |
84 | { | 82 | { |
85 | QStandardPaths::setTestModeEnabled(enabled); | 83 | QStandardPaths::setTestModeEnabled(enabled); |
84 | Sink::clearLocationCache(); | ||
86 | if (enabled) { | 85 | if (enabled) { |
87 | qputenv("SINK_TESTMODE", "TRUE"); | 86 | qputenv("SINK_TESTMODE", "TRUE"); |
88 | } else { | 87 | } else { |
diff --git a/common/typeindex.cpp b/common/typeindex.cpp index 153aa43..5a19839 100644 --- a/common/typeindex.cpp +++ b/common/typeindex.cpp | |||
@@ -21,18 +21,20 @@ | |||
21 | #include "log.h" | 21 | #include "log.h" |
22 | #include "index.h" | 22 | #include "index.h" |
23 | #include <QDateTime> | 23 | #include <QDateTime> |
24 | 24 | #include <QDataStream> | |
25 | SINK_DEBUG_AREA("typeindex") | ||
26 | 25 | ||
27 | using namespace Sink; | 26 | using namespace Sink; |
28 | 27 | ||
29 | static QByteArray getByteArray(const QVariant &value) | 28 | static QByteArray getByteArray(const QVariant &value) |
30 | { | 29 | { |
31 | if (value.type() == QVariant::DateTime) { | 30 | if (value.type() == QVariant::DateTime) { |
32 | const auto result = value.toDateTime().toString().toLatin1(); | 31 | QByteArray result; |
33 | if (result.isEmpty()) { | 32 | QDataStream ds(&result, QIODevice::WriteOnly); |
34 | return "nodate"; | 33 | ds << value.toDateTime(); |
35 | } | 34 | return result; |
35 | } | ||
36 | if (value.type() == QVariant::Bool) { | ||
37 | return value.toBool() ? "t" : "f"; | ||
36 | } | 38 | } |
37 | if (value.canConvert<Sink::ApplicationDomain::Reference>()) { | 39 | if (value.canConvert<Sink::ApplicationDomain::Reference>()) { |
38 | const auto ba = value.value<Sink::ApplicationDomain::Reference>().value; | 40 | const auto ba = value.value<Sink::ApplicationDomain::Reference>().value; |
@@ -85,6 +87,20 @@ void TypeIndex::addProperty<QByteArray>(const QByteArray &property) | |||
85 | } | 87 | } |
86 | 88 | ||
87 | template <> | 89 | template <> |
90 | void TypeIndex::addProperty<bool>(const QByteArray &property) | ||
91 | { | ||
92 | auto indexer = [this, property](bool add, const QByteArray &identifier, const QVariant &value, Sink::Storage::DataStore::Transaction &transaction) { | ||
93 | if (add) { | ||
94 | Index(indexName(property), transaction).add(getByteArray(value), identifier); | ||
95 | } else { | ||
96 | Index(indexName(property), transaction).remove(getByteArray(value), identifier); | ||
97 | } | ||
98 | }; | ||
99 | mIndexer.insert(property, indexer); | ||
100 | mProperties << property; | ||
101 | } | ||
102 | |||
103 | template <> | ||
88 | void TypeIndex::addProperty<QString>(const QByteArray &property) | 104 | void TypeIndex::addProperty<QString>(const QByteArray &property) |
89 | { | 105 | { |
90 | auto indexer = [this, property](bool add, const QByteArray &identifier, const QVariant &value, Sink::Storage::DataStore::Transaction &transaction) { | 106 | auto indexer = [this, property](bool add, const QByteArray &identifier, const QVariant &value, Sink::Storage::DataStore::Transaction &transaction) { |
@@ -266,6 +282,18 @@ void TypeIndex::index<QString, QByteArray>(const QByteArray &leftName, const QBy | |||
266 | } | 282 | } |
267 | 283 | ||
268 | template <> | 284 | template <> |
285 | void TypeIndex::unindex<QByteArray, QByteArray>(const QByteArray &leftName, const QByteArray &rightName, const QVariant &leftValue, const QVariant &rightValue, Sink::Storage::DataStore::Transaction &transaction) | ||
286 | { | ||
287 | Index(indexName(leftName + rightName), transaction).remove(getByteArray(leftValue), getByteArray(rightValue)); | ||
288 | } | ||
289 | |||
290 | template <> | ||
291 | void TypeIndex::unindex<QString, QByteArray>(const QByteArray &leftName, const QByteArray &rightName, const QVariant &leftValue, const QVariant &rightValue, Sink::Storage::DataStore::Transaction &transaction) | ||
292 | { | ||
293 | Index(indexName(leftName + rightName), transaction).remove(getByteArray(leftValue), getByteArray(rightValue)); | ||
294 | } | ||
295 | |||
296 | template <> | ||
269 | QVector<QByteArray> TypeIndex::secondaryLookup<QByteArray>(const QByteArray &leftName, const QByteArray &rightName, const QVariant &value) | 297 | QVector<QByteArray> TypeIndex::secondaryLookup<QByteArray>(const QByteArray &leftName, const QByteArray &rightName, const QVariant &value) |
270 | { | 298 | { |
271 | QVector<QByteArray> keys; | 299 | QVector<QByteArray> keys; |
diff --git a/common/typeindex.h b/common/typeindex.h index 1f216a7..890c3db 100644 --- a/common/typeindex.h +++ b/common/typeindex.h | |||
@@ -95,6 +95,15 @@ public: | |||
95 | template <typename LeftType, typename RightType> | 95 | template <typename LeftType, typename RightType> |
96 | void index(const QByteArray &leftName, const QByteArray &rightName, const QVariant &leftValue, const QVariant &rightValue, Sink::Storage::DataStore::Transaction &transaction); | 96 | void index(const QByteArray &leftName, const QByteArray &rightName, const QVariant &leftValue, const QVariant &rightValue, Sink::Storage::DataStore::Transaction &transaction); |
97 | 97 | ||
98 | template <typename Left, typename Right> | ||
99 | void unindex(const QVariant &leftValue, const QVariant &rightValue, Sink::Storage::DataStore::Transaction &transaction) | ||
100 | { | ||
101 | index<typename Left::Type, typename Right::Type>(Left::name, Right::name, leftValue, rightValue, transaction); | ||
102 | } | ||
103 | |||
104 | template <typename LeftType, typename RightType> | ||
105 | void unindex(const QByteArray &leftName, const QByteArray &rightName, const QVariant &leftValue, const QVariant &rightValue, Sink::Storage::DataStore::Transaction &transaction); | ||
106 | |||
98 | 107 | ||
99 | private: | 108 | private: |
100 | friend class Sink::Storage::EntityStore; | 109 | friend class Sink::Storage::EntityStore; |
diff --git a/dist/sink.spec b/dist/sink.spec index 60015f7..f5132b7 100644 --- a/dist/sink.spec +++ b/dist/sink.spec | |||
@@ -1,7 +1,7 @@ | |||
1 | 1 | ||
2 | Name: sink | 2 | Name: sink |
3 | Version: 0.2.0 | 3 | Version: 0.3 |
4 | Release: 0%{?dist} | 4 | Release: 10%{?dist} |
5 | Summary: sink | 5 | Summary: sink |
6 | 6 | ||
7 | Group: Applications/Desktop | 7 | Group: Applications/Desktop |
@@ -18,6 +18,7 @@ BuildRequires: kf5-kcoreaddons-devel | |||
18 | BuildRequires: kf5-kcontacts-devel | 18 | BuildRequires: kf5-kcontacts-devel |
19 | BuildRequires: kf5-kmime-devel | 19 | BuildRequires: kf5-kmime-devel |
20 | BuildRequires: kimap2-devel | 20 | BuildRequires: kimap2-devel |
21 | BuildRequires: kdav2-devel | ||
21 | BuildRequires: libcurl-devel | 22 | BuildRequires: libcurl-devel |
22 | BuildRequires: libgit2-devel | 23 | BuildRequires: libgit2-devel |
23 | BuildRequires: lmdb-devel | 24 | BuildRequires: lmdb-devel |
@@ -37,17 +38,11 @@ Development headers for sink | |||
37 | %prep | 38 | %prep |
38 | %setup -q | 39 | %setup -q |
39 | 40 | ||
40 | sed -i \ | ||
41 | -e '/inspectiontest/d' \ | ||
42 | -e '/maildirresourcetest/d' \ | ||
43 | tests/CMakeLists.txt | ||
44 | |||
45 | %build | 41 | %build |
46 | mkdir -p build/ | 42 | mkdir -p build/ |
47 | pushd build | 43 | pushd build |
48 | %{cmake} \ | 44 | %{cmake} \ |
49 | -DQT_PLUGIN_INSTALL_DIR:PATH=%{_libdir}/qt5/plugins/ \ | 45 | -DQT_PLUGIN_INSTALL_DIR:PATH=%{_libdir}/qt5/plugins/ \ |
50 | -DBUILD_DAV=OFF \ | ||
51 | .. | 46 | .. |
52 | 47 | ||
53 | make %{?_smp_mflags} | 48 | make %{?_smp_mflags} |
@@ -58,7 +53,6 @@ pushd build | |||
58 | %make_install | 53 | %make_install |
59 | popd | 54 | popd |
60 | 55 | ||
61 | #rm -rf %{buildroot}%{_prefix}/mkspecs/modules/qt_KMime.pri | ||
62 | rm %{buildroot}%{_prefix}/bin/resetmailbox.sh | 56 | rm %{buildroot}%{_prefix}/bin/resetmailbox.sh |
63 | rm %{buildroot}%{_prefix}/bin/populatemailbox.sh | 57 | rm %{buildroot}%{_prefix}/bin/populatemailbox.sh |
64 | rm %{buildroot}%{_prefix}/bin/sink_smtp_test | 58 | rm %{buildroot}%{_prefix}/bin/sink_smtp_test |
diff --git a/examples/davresource/CMakeLists.txt b/examples/davresource/CMakeLists.txt index 28829d5..7091edc 100644 --- a/examples/davresource/CMakeLists.txt +++ b/examples/davresource/CMakeLists.txt | |||
@@ -3,10 +3,10 @@ project(sink_resource_dav) | |||
3 | add_definitions(-DQT_PLUGIN) | 3 | add_definitions(-DQT_PLUGIN) |
4 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) | 4 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) |
5 | 5 | ||
6 | find_package(KPimKDAV REQUIRED) | 6 | find_package(KPimKDAV2 REQUIRED) |
7 | 7 | ||
8 | add_library(${PROJECT_NAME} SHARED davresource.cpp) | 8 | add_library(${PROJECT_NAME} SHARED davresource.cpp) |
9 | qt5_use_modules(${PROJECT_NAME} Core Network) | 9 | qt5_use_modules(${PROJECT_NAME} Core Network) |
10 | target_link_libraries(${PROJECT_NAME} sink KPim::KDAV) | 10 | target_link_libraries(${PROJECT_NAME} sink KPim::KDAV2) |
11 | 11 | ||
12 | install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SINK_RESOURCE_PLUGINS_PATH}) | 12 | install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SINK_RESOURCE_PLUGINS_PATH}) |
diff --git a/examples/davresource/davresource.cpp b/examples/davresource/davresource.cpp index 50471ed..465220f 100644 --- a/examples/davresource/davresource.cpp +++ b/examples/davresource/davresource.cpp | |||
@@ -31,19 +31,17 @@ | |||
31 | 31 | ||
32 | #include "contactpreprocessor.h" | 32 | #include "contactpreprocessor.h" |
33 | 33 | ||
34 | #include <KDAV/DavCollection> | 34 | #include <KDAV2/DavCollection> |
35 | #include <KDAV/DavCollectionsFetchJob> | 35 | #include <KDAV2/DavCollectionsFetchJob> |
36 | #include <KDAV/DavItem> | 36 | #include <KDAV2/DavItem> |
37 | #include <KDAV/DavItemsListJob> | 37 | #include <KDAV2/DavItemsListJob> |
38 | #include <KDAV/DavItemFetchJob> | 38 | #include <KDAV2/DavItemFetchJob> |
39 | #include <KDAV/EtagCache> | 39 | #include <KDAV2/EtagCache> |
40 | 40 | ||
41 | //This is the resources entity type, and not the domain type | 41 | //This is the resources entity type, and not the domain type |
42 | #define ENTITY_TYPE_CONTACT "contact" | 42 | #define ENTITY_TYPE_CONTACT "contact" |
43 | #define ENTITY_TYPE_ADDRESSBOOK "addressbook" | 43 | #define ENTITY_TYPE_ADDRESSBOOK "addressbook" |
44 | 44 | ||
45 | SINK_DEBUG_AREA("davresource") | ||
46 | |||
47 | using namespace Sink; | 45 | using namespace Sink; |
48 | 46 | ||
49 | static KAsync::Job<void> runJob(KJob *job) | 47 | static KAsync::Job<void> runJob(KJob *job) |
@@ -87,7 +85,7 @@ public: | |||
87 | return remoteId; | 85 | return remoteId; |
88 | } | 86 | } |
89 | 87 | ||
90 | void synchronizeAddressbooks(const KDAV::DavCollection::List &addressbookList) | 88 | void synchronizeAddressbooks(const KDAV2::DavCollection::List &addressbookList) |
91 | { | 89 | { |
92 | const QByteArray bufferType = ENTITY_TYPE_ADDRESSBOOK; | 90 | const QByteArray bufferType = ENTITY_TYPE_ADDRESSBOOK; |
93 | SinkTrace() << "Found addressbooks " << addressbookList.size(); | 91 | SinkTrace() << "Found addressbooks " << addressbookList.size(); |
@@ -121,12 +119,12 @@ public: | |||
121 | return list; | 119 | return list; |
122 | } | 120 | } |
123 | 121 | ||
124 | static QByteArray getRid(const KDAV::DavItem &item) | 122 | static QByteArray getRid(const KDAV2::DavItem &item) |
125 | { | 123 | { |
126 | return item.url().toDisplayString().toUtf8(); | 124 | return item.url().toDisplayString().toUtf8(); |
127 | } | 125 | } |
128 | 126 | ||
129 | static QByteArray getRid(const KDAV::DavCollection &item) | 127 | static QByteArray getRid(const KDAV2::DavCollection &item) |
130 | { | 128 | { |
131 | return item.url().toDisplayString().toUtf8(); | 129 | return item.url().toDisplayString().toUtf8(); |
132 | } | 130 | } |
@@ -135,7 +133,7 @@ public: | |||
135 | { | 133 | { |
136 | if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Addressbook>()) { | 134 | if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Addressbook>()) { |
137 | SinkLogCtx(mLogCtx) << "Synchronizing addressbooks:" << mResourceUrl.url(); | 135 | SinkLogCtx(mLogCtx) << "Synchronizing addressbooks:" << mResourceUrl.url(); |
138 | auto collectionsFetchJob = new KDAV::DavCollectionsFetchJob(mResourceUrl); | 136 | auto collectionsFetchJob = new KDAV2::DavCollectionsFetchJob(mResourceUrl); |
139 | auto job = runJob(collectionsFetchJob).then([this, collectionsFetchJob] (const KAsync::Error &error) { | 137 | auto job = runJob(collectionsFetchJob).then([this, collectionsFetchJob] (const KAsync::Error &error) { |
140 | if (error) { | 138 | if (error) { |
141 | SinkWarningCtx(mLogCtx) << "Failed to synchronize addressbooks." << collectionsFetchJob->errorString(); | 139 | SinkWarningCtx(mLogCtx) << "Failed to synchronize addressbooks." << collectionsFetchJob->errorString(); |
@@ -147,29 +145,29 @@ public: | |||
147 | } else if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Contact>()) { | 145 | } else if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Contact>()) { |
148 | SinkLogCtx(mLogCtx) << "Synchronizing contacts."; | 146 | SinkLogCtx(mLogCtx) << "Synchronizing contacts."; |
149 | auto ridList = QSharedPointer<QByteArrayList>::create(); | 147 | auto ridList = QSharedPointer<QByteArrayList>::create(); |
150 | auto collectionsFetchJob = new KDAV::DavCollectionsFetchJob(mResourceUrl); | 148 | auto collectionsFetchJob = new KDAV2::DavCollectionsFetchJob(mResourceUrl); |
151 | auto job = runJob(collectionsFetchJob).then([this, collectionsFetchJob] { | 149 | auto job = runJob(collectionsFetchJob).then([this, collectionsFetchJob] { |
152 | synchronizeAddressbooks(collectionsFetchJob ->collections()); | 150 | synchronizeAddressbooks(collectionsFetchJob ->collections()); |
153 | return collectionsFetchJob->collections(); | 151 | return collectionsFetchJob->collections(); |
154 | }) | 152 | }) |
155 | .serialEach([this, ridList](const KDAV::DavCollection &collection) { | 153 | .serialEach([this, ridList](const KDAV2::DavCollection &collection) { |
156 | auto collId = getRid(collection); | 154 | auto collId = getRid(collection); |
157 | const auto addressbookLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_ADDRESSBOOK, collId); | 155 | const auto addressbookLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_ADDRESSBOOK, collId); |
158 | auto ctag = collection.CTag().toLatin1(); | 156 | auto ctag = collection.CTag().toLatin1(); |
159 | if (ctag != syncStore().readValue(collId + "_ctagXX")) { | 157 | if (ctag != syncStore().readValue(collId + "_ctagXX")) { |
160 | SinkTraceCtx(mLogCtx) << "Syncing " << collId; | 158 | SinkTraceCtx(mLogCtx) << "Syncing " << collId; |
161 | auto cache = std::shared_ptr<KDAV::EtagCache>(new KDAV::EtagCache()); | 159 | auto cache = std::shared_ptr<KDAV2::EtagCache>(new KDAV2::EtagCache()); |
162 | auto davItemsListJob = new KDAV::DavItemsListJob(collection.url(), cache); | 160 | auto davItemsListJob = new KDAV2::DavItemsListJob(collection.url(), cache); |
163 | const QByteArray bufferType = ENTITY_TYPE_CONTACT; | 161 | const QByteArray bufferType = ENTITY_TYPE_CONTACT; |
164 | QHash<QByteArray, Query::Comparator> mergeCriteria; | 162 | QHash<QByteArray, Query::Comparator> mergeCriteria; |
165 | auto colljob = runJob(davItemsListJob).then([davItemsListJob] { | 163 | auto colljob = runJob(davItemsListJob).then([davItemsListJob] { |
166 | return KAsync::value(davItemsListJob->items()); | 164 | return KAsync::value(davItemsListJob->items()); |
167 | }) | 165 | }) |
168 | .serialEach([=] (const KDAV::DavItem &item) { | 166 | .serialEach([=] (const KDAV2::DavItem &item) { |
169 | QByteArray rid = getRid(item); | 167 | QByteArray rid = getRid(item); |
170 | if (item.etag().toLatin1() != syncStore().readValue(rid + "_etag")){ | 168 | if (item.etag().toLatin1() != syncStore().readValue(rid + "_etag")){ |
171 | SinkTrace() << "Updating " << rid; | 169 | SinkTrace() << "Updating " << rid; |
172 | auto davItemFetchJob = new KDAV::DavItemFetchJob(item); | 170 | auto davItemFetchJob = new KDAV2::DavItemFetchJob(item); |
173 | auto itemjob = runJob(davItemFetchJob) | 171 | auto itemjob = runJob(davItemFetchJob) |
174 | .then([=] { | 172 | .then([=] { |
175 | const auto item = davItemFetchJob->item(); | 173 | const auto item = davItemFetchJob->item(); |
@@ -180,7 +178,7 @@ public: | |||
180 | createOrModify(bufferType, rid, contact, mergeCriteria); | 178 | createOrModify(bufferType, rid, contact, mergeCriteria); |
181 | return item; | 179 | return item; |
182 | }) | 180 | }) |
183 | .then([this, ridList] (const KDAV::DavItem &item) { | 181 | .then([this, ridList] (const KDAV2::DavItem &item) { |
184 | const auto rid = getRid(item); | 182 | const auto rid = getRid(item); |
185 | syncStore().writeValue(rid + "_etag", item.etag().toLatin1()); | 183 | syncStore().writeValue(rid + "_etag", item.etag().toLatin1()); |
186 | ridList->append(rid); | 184 | ridList->append(rid); |
@@ -227,24 +225,19 @@ KAsync::Job<QByteArray> replay(const ApplicationDomain::Contact &contact, Sink:: | |||
227 | } | 225 | } |
228 | 226 | ||
229 | public: | 227 | public: |
230 | KDAV::DavUrl mResourceUrl; | 228 | KDAV2::DavUrl mResourceUrl; |
231 | }; | 229 | }; |
232 | 230 | ||
233 | 231 | ||
234 | DavResource::DavResource(const Sink::ResourceContext &resourceContext) | 232 | DavResource::DavResource(const Sink::ResourceContext &resourceContext) |
235 | : Sink::GenericResource(resourceContext) | 233 | : Sink::GenericResource(resourceContext) |
236 | { | 234 | { |
237 | /* | ||
238 | * Fork KIO slaves (used in kdav), instead of starting them via klauncher. | ||
239 | * Otherwise we have yet another runtime dependency that will i.e. not work in the docker container. | ||
240 | */ | ||
241 | qputenv("KDE_FORK_SLAVES", "TRUE"); | ||
242 | auto config = ResourceConfig::getConfiguration(resourceContext.instanceId()); | 235 | auto config = ResourceConfig::getConfiguration(resourceContext.instanceId()); |
243 | auto resourceUrl = QUrl::fromUserInput(config.value("server").toString()); | 236 | auto resourceUrl = QUrl::fromUserInput(config.value("server").toString()); |
244 | resourceUrl.setUserName(config.value("username").toString()); | 237 | resourceUrl.setUserName(config.value("username").toString()); |
245 | resourceUrl.setPassword(config.value("password").toString()); | 238 | resourceUrl.setPassword(config.value("password").toString()); |
246 | 239 | ||
247 | mResourceUrl = KDAV::DavUrl(resourceUrl, KDAV::CardDav); | 240 | mResourceUrl = KDAV2::DavUrl(resourceUrl, KDAV2::CardDav); |
248 | 241 | ||
249 | auto synchronizer = QSharedPointer<ContactSynchronizer>::create(resourceContext); | 242 | auto synchronizer = QSharedPointer<ContactSynchronizer>::create(resourceContext); |
250 | synchronizer->mResourceUrl = mResourceUrl; | 243 | synchronizer->mResourceUrl = mResourceUrl; |
@@ -271,7 +264,7 @@ Sink::Resource *DavResourceFactory::createResource(const ResourceContext &contex | |||
271 | void DavResourceFactory::registerFacades(const QByteArray &name, Sink::FacadeFactory &factory) | 264 | void DavResourceFactory::registerFacades(const QByteArray &name, Sink::FacadeFactory &factory) |
272 | { | 265 | { |
273 | factory.registerFacade<ApplicationDomain::Contact, DefaultFacade<ApplicationDomain::Contact>>(name); | 266 | factory.registerFacade<ApplicationDomain::Contact, DefaultFacade<ApplicationDomain::Contact>>(name); |
274 | factory.registerFacade<ApplicationDomain::Addressbook, DefaultFacade<ApplicationDomain::Contact>>(name); | 267 | factory.registerFacade<ApplicationDomain::Addressbook, DefaultFacade<ApplicationDomain::Addressbook>>(name); |
275 | } | 268 | } |
276 | 269 | ||
277 | void DavResourceFactory::registerAdaptorFactories(const QByteArray &name, Sink::AdaptorFactoryRegistry ®istry) | 270 | void DavResourceFactory::registerAdaptorFactories(const QByteArray &name, Sink::AdaptorFactoryRegistry ®istry) |
diff --git a/examples/davresource/davresource.h b/examples/davresource/davresource.h index 1ce66ea..db175a4 100644 --- a/examples/davresource/davresource.h +++ b/examples/davresource/davresource.h | |||
@@ -21,7 +21,7 @@ | |||
21 | 21 | ||
22 | #include "common/genericresource.h" | 22 | #include "common/genericresource.h" |
23 | 23 | ||
24 | #include <KDAV/DavUrl> | 24 | #include <KDAV2/DavUrl> |
25 | #include <KAsync/Async> | 25 | #include <KAsync/Async> |
26 | 26 | ||
27 | #include <flatbuffers/flatbuffers.h> | 27 | #include <flatbuffers/flatbuffers.h> |
@@ -48,7 +48,7 @@ public: | |||
48 | private: | 48 | private: |
49 | QStringList listAvailableFolders(); | 49 | QStringList listAvailableFolders(); |
50 | 50 | ||
51 | KDAV::DavUrl mResourceUrl; | 51 | KDAV2::DavUrl mResourceUrl; |
52 | }; | 52 | }; |
53 | 53 | ||
54 | class DavResourceFactory : public Sink::ResourceFactory | 54 | class DavResourceFactory : public Sink::ResourceFactory |
diff --git a/examples/dummyresource/domainadaptor.cpp b/examples/dummyresource/domainadaptor.cpp index dcc08c7..e7a20da 100644 --- a/examples/dummyresource/domainadaptor.cpp +++ b/examples/dummyresource/domainadaptor.cpp | |||
@@ -28,9 +28,6 @@ using namespace flatbuffers; | |||
28 | DummyEventAdaptorFactory::DummyEventAdaptorFactory() | 28 | DummyEventAdaptorFactory::DummyEventAdaptorFactory() |
29 | : DomainTypeAdaptorFactory() | 29 | : DomainTypeAdaptorFactory() |
30 | { | 30 | { |
31 | //TODO turn this into initializeReadPropertyMapper as well? | ||
32 | mResourceMapper->addMapping<Sink::ApplicationDomain::Event::Summary, DummyEvent>(&DummyEvent::summary); | ||
33 | mResourceWriteMapper->addMapping<Sink::ApplicationDomain::Event::Summary>(&DummyEventBuilder::add_summary); | ||
34 | } | 31 | } |
35 | 32 | ||
36 | DummyMailAdaptorFactory::DummyMailAdaptorFactory() | 33 | DummyMailAdaptorFactory::DummyMailAdaptorFactory() |
diff --git a/examples/dummyresource/domainadaptor.h b/examples/dummyresource/domainadaptor.h index e7098e9..3faaa63 100644 --- a/examples/dummyresource/domainadaptor.h +++ b/examples/dummyresource/domainadaptor.h | |||
@@ -22,25 +22,23 @@ | |||
22 | #include "event_generated.h" | 22 | #include "event_generated.h" |
23 | #include "mail_generated.h" | 23 | #include "mail_generated.h" |
24 | #include "folder_generated.h" | 24 | #include "folder_generated.h" |
25 | #include "dummy_generated.h" | ||
26 | #include "dummycalendar_generated.h" | ||
27 | #include "entity_generated.h" | 25 | #include "entity_generated.h" |
28 | 26 | ||
29 | class DummyEventAdaptorFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Event, DummyCalendar::DummyEvent, DummyCalendar::DummyEventBuilder> | 27 | class DummyEventAdaptorFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Event> |
30 | { | 28 | { |
31 | public: | 29 | public: |
32 | DummyEventAdaptorFactory(); | 30 | DummyEventAdaptorFactory(); |
33 | virtual ~DummyEventAdaptorFactory() {}; | 31 | virtual ~DummyEventAdaptorFactory() {}; |
34 | }; | 32 | }; |
35 | 33 | ||
36 | class DummyMailAdaptorFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Mail, Sink::ApplicationDomain::Buffer::Dummy, Sink::ApplicationDomain::Buffer::DummyBuilder> | 34 | class DummyMailAdaptorFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Mail> |
37 | { | 35 | { |
38 | public: | 36 | public: |
39 | DummyMailAdaptorFactory(); | 37 | DummyMailAdaptorFactory(); |
40 | virtual ~DummyMailAdaptorFactory() {}; | 38 | virtual ~DummyMailAdaptorFactory() {}; |
41 | }; | 39 | }; |
42 | 40 | ||
43 | class DummyFolderAdaptorFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Folder, Sink::ApplicationDomain::Buffer::Dummy, Sink::ApplicationDomain::Buffer::DummyBuilder> | 41 | class DummyFolderAdaptorFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Folder> |
44 | { | 42 | { |
45 | public: | 43 | public: |
46 | DummyFolderAdaptorFactory(); | 44 | DummyFolderAdaptorFactory(); |
diff --git a/examples/dummyresource/resourcefactory.cpp b/examples/dummyresource/resourcefactory.cpp index c1f536e..dffdfc9 100644 --- a/examples/dummyresource/resourcefactory.cpp +++ b/examples/dummyresource/resourcefactory.cpp | |||
@@ -42,8 +42,6 @@ | |||
42 | #define ENTITY_TYPE_MAIL "mail" | 42 | #define ENTITY_TYPE_MAIL "mail" |
43 | #define ENTITY_TYPE_FOLDER "folder" | 43 | #define ENTITY_TYPE_FOLDER "folder" |
44 | 44 | ||
45 | SINK_DEBUG_AREA("dummyresource") | ||
46 | |||
47 | using namespace Sink; | 45 | using namespace Sink; |
48 | 46 | ||
49 | class DummySynchronizer : public Sink::Synchronizer { | 47 | class DummySynchronizer : public Sink::Synchronizer { |
diff --git a/examples/imapresource/CMakeLists.txt b/examples/imapresource/CMakeLists.txt index 46a8b08..5d2d38b 100644 --- a/examples/imapresource/CMakeLists.txt +++ b/examples/imapresource/CMakeLists.txt | |||
@@ -4,7 +4,7 @@ add_definitions(-DQT_PLUGIN) | |||
4 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) | 4 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) |
5 | 5 | ||
6 | find_package(KF5 COMPONENTS REQUIRED Mime) | 6 | find_package(KF5 COMPONENTS REQUIRED Mime) |
7 | find_package(KIMAP2 0.0.1 REQUIRED) | 7 | find_package(KIMAP2 0.2 REQUIRED) |
8 | 8 | ||
9 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) | 9 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) |
10 | 10 | ||
diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index 0579dae..81c808b 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp | |||
@@ -46,8 +46,6 @@ | |||
46 | #define ENTITY_TYPE_MAIL "mail" | 46 | #define ENTITY_TYPE_MAIL "mail" |
47 | #define ENTITY_TYPE_FOLDER "folder" | 47 | #define ENTITY_TYPE_FOLDER "folder" |
48 | 48 | ||
49 | SINK_DEBUG_AREA("imapresource") | ||
50 | |||
51 | Q_DECLARE_METATYPE(QSharedPointer<Imap::ImapServerProxy>) | 49 | Q_DECLARE_METATYPE(QSharedPointer<Imap::ImapServerProxy>) |
52 | 50 | ||
53 | using namespace Imap; | 51 | using namespace Imap; |
@@ -87,6 +85,25 @@ static QByteArray parentRid(const Imap::Folder &folder) | |||
87 | return folder.parentPath().toUtf8(); | 85 | return folder.parentPath().toUtf8(); |
88 | } | 86 | } |
89 | 87 | ||
88 | static QByteArray getSpecialPurposeType(const QByteArrayList &flags) | ||
89 | { | ||
90 | if (Imap::flagsContain(Imap::FolderFlags::Trash, flags)) { | ||
91 | return ApplicationDomain::SpecialPurpose::Mail::trash; | ||
92 | } | ||
93 | if (Imap::flagsContain(Imap::FolderFlags::Drafts, flags)) { | ||
94 | return ApplicationDomain::SpecialPurpose::Mail::drafts; | ||
95 | } | ||
96 | if (Imap::flagsContain(Imap::FolderFlags::Sent, flags)) { | ||
97 | return ApplicationDomain::SpecialPurpose::Mail::sent; | ||
98 | } | ||
99 | return {}; | ||
100 | } | ||
101 | |||
102 | static bool hasSpecialPurposeFlag(const QByteArrayList &flags) | ||
103 | { | ||
104 | return !getSpecialPurposeType(flags).isEmpty(); | ||
105 | } | ||
106 | |||
90 | 107 | ||
91 | class ImapSynchronizer : public Sink::Synchronizer { | 108 | class ImapSynchronizer : public Sink::Synchronizer { |
92 | Q_OBJECT | 109 | Q_OBJECT |
@@ -100,22 +117,29 @@ public: | |||
100 | QByteArray createFolder(const Imap::Folder &f) | 117 | QByteArray createFolder(const Imap::Folder &f) |
101 | { | 118 | { |
102 | const auto parentFolderRid = parentRid(f); | 119 | const auto parentFolderRid = parentRid(f); |
103 | SinkTraceCtx(mLogCtx) << "Creating folder: " << f.name() << parentFolderRid; | 120 | bool isToplevel = parentFolderRid.isEmpty(); |
121 | |||
122 | SinkTraceCtx(mLogCtx) << "Creating folder: " << f.name() << parentFolderRid << f.flags; | ||
104 | 123 | ||
105 | const auto remoteId = folderRid(f); | 124 | const auto remoteId = folderRid(f); |
106 | Sink::ApplicationDomain::Folder folder; | 125 | Sink::ApplicationDomain::Folder folder; |
107 | folder.setName(f.name()); | 126 | folder.setName(f.name()); |
108 | folder.setIcon("folder"); | 127 | folder.setIcon("folder"); |
109 | folder.setEnabled(f.subscribed); | 128 | folder.setEnabled(f.subscribed); |
110 | QHash<QByteArray, Query::Comparator> mergeCriteria; | 129 | auto specialPurpose = [&] { |
111 | if (SpecialPurpose::isSpecialPurposeFolderName(f.name()) && parentFolderRid.isEmpty()) { | 130 | if (hasSpecialPurposeFlag(f.flags)) { |
112 | auto type = SpecialPurpose::getSpecialPurposeType(f.name()); | 131 | return getSpecialPurposeType(f.flags); |
113 | folder.setSpecialPurpose(QByteArrayList() << type); | 132 | } else if (SpecialPurpose::isSpecialPurposeFolderName(f.name()) && isToplevel) { |
114 | mergeCriteria.insert(ApplicationDomain::Folder::SpecialPurpose::name, Query::Comparator(type, Query::Comparator::Contains)); | 133 | return SpecialPurpose::getSpecialPurposeType(f.name()); |
134 | } | ||
135 | return QByteArray{}; | ||
136 | }(); | ||
137 | if (!specialPurpose.isEmpty()) { | ||
138 | folder.setSpecialPurpose(QByteArrayList() << specialPurpose); | ||
115 | } | 139 | } |
116 | 140 | ||
117 | if (!parentFolderRid.isEmpty()) { | 141 | if (!isToplevel) { |
118 | folder.setParent(syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, parentFolderRid)); | 142 | folder.setParent(syncStore().resolveRemoteId(ApplicationDomain::Folder::name, parentFolderRid)); |
119 | } | 143 | } |
120 | createOrModify(ApplicationDomain::getTypeName<ApplicationDomain::Folder>(), remoteId, folder); | 144 | createOrModify(ApplicationDomain::getTypeName<ApplicationDomain::Folder>(), remoteId, folder); |
121 | return remoteId; | 145 | return remoteId; |
@@ -160,22 +184,20 @@ public: | |||
160 | return flags; | 184 | return flags; |
161 | } | 185 | } |
162 | 186 | ||
163 | void synchronizeMails(const QByteArray &folderRid, const Message &message) | 187 | void synchronizeMails(const QByteArray &folderRid, const QByteArray &folderLocalId, const Message &message) |
164 | { | 188 | { |
165 | auto time = QSharedPointer<QTime>::create(); | 189 | auto time = QSharedPointer<QTime>::create(); |
166 | time->start(); | 190 | time->start(); |
167 | SinkTraceCtx(mLogCtx) << "Importing new mail." << folderRid; | 191 | SinkTraceCtx(mLogCtx) << "Importing new mail." << folderRid; |
168 | 192 | ||
169 | const auto folderLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, folderRid); | ||
170 | |||
171 | const auto remoteId = assembleMailRid(folderLocalId, message.uid); | 193 | const auto remoteId = assembleMailRid(folderLocalId, message.uid); |
172 | 194 | ||
173 | Q_ASSERT(message.msg); | 195 | Q_ASSERT(message.msg); |
174 | SinkTraceCtx(mLogCtx) << "Found a mail " << remoteId << message.msg->subject(true)->asUnicodeString() << message.flags; | 196 | SinkTraceCtx(mLogCtx) << "Found a mail " << remoteId << message.flags; |
175 | 197 | ||
176 | auto mail = Sink::ApplicationDomain::Mail::create(mResourceInstanceIdentifier); | 198 | auto mail = Sink::ApplicationDomain::Mail::create(mResourceInstanceIdentifier); |
177 | mail.setFolder(folderLocalId); | 199 | mail.setFolder(folderLocalId); |
178 | mail.setMimeMessage(message.msg->encodedContent()); | 200 | mail.setMimeMessage(message.msg->encodedContent(true)); |
179 | mail.setExtractedFullPayloadAvailable(message.fullPayload); | 201 | mail.setExtractedFullPayloadAvailable(message.fullPayload); |
180 | setFlags(mail, message.flags); | 202 | setFlags(mail, message.flags); |
181 | 203 | ||
@@ -291,14 +313,15 @@ public: | |||
291 | SinkTraceCtx(mLogCtx) << "Uids to fetch: " << filteredAndSorted; | 313 | SinkTraceCtx(mLogCtx) << "Uids to fetch: " << filteredAndSorted; |
292 | 314 | ||
293 | bool headersOnly = false; | 315 | bool headersOnly = false; |
316 | const auto folderLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, folderRemoteId); | ||
294 | return imap->fetchMessages(folder, filteredAndSorted, headersOnly, [=](const Message &m) { | 317 | return imap->fetchMessages(folder, filteredAndSorted, headersOnly, [=](const Message &m) { |
295 | if (*maxUid < m.uid) { | 318 | if (*maxUid < m.uid) { |
296 | *maxUid = m.uid; | 319 | *maxUid = m.uid; |
297 | } | 320 | } |
298 | synchronizeMails(folderRemoteId, m); | 321 | synchronizeMails(folderRemoteId, folderLocalId, m); |
299 | }, | 322 | }, |
300 | [this, maxUid, folder](int progress, int total) { | 323 | [=](int progress, int total) { |
301 | SinkLog() << "Progress: " << progress << " out of " << total; | 324 | reportProgress(progress, total, QByteArrayList{} << folderLocalId); |
302 | //commit every 10 messages | 325 | //commit every 10 messages |
303 | if ((progress % 10) == 0) { | 326 | if ((progress % 10) == 0) { |
304 | commit(); | 327 | commit(); |
@@ -335,11 +358,12 @@ public: | |||
335 | SinkLogCtx(mLogCtx) << "Fetching headers for: " << toFetch; | 358 | SinkLogCtx(mLogCtx) << "Fetching headers for: " << toFetch; |
336 | 359 | ||
337 | bool headersOnly = true; | 360 | bool headersOnly = true; |
361 | const auto folderLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, folderRemoteId); | ||
338 | return imap->fetchMessages(folder, toFetch, headersOnly, [=](const Message &m) { | 362 | return imap->fetchMessages(folder, toFetch, headersOnly, [=](const Message &m) { |
339 | synchronizeMails(folderRemoteId, m); | 363 | synchronizeMails(folderRemoteId, folderLocalId, m); |
340 | }, | 364 | }, |
341 | [=](int progress, int total) { | 365 | [=](int progress, int total) { |
342 | SinkLogCtx(mLogCtx) << "Progress: " << progress << " out of " << total; | 366 | reportProgress(progress, total, QByteArrayList{} << folderLocalId); |
343 | //commit every 100 messages | 367 | //commit every 100 messages |
344 | if ((progress % 100) == 0) { | 368 | if ((progress % 100) == 0) { |
345 | commit(); | 369 | commit(); |
@@ -466,7 +490,7 @@ public: | |||
466 | //Otherwise fetch full payload for daterange | 490 | //Otherwise fetch full payload for daterange |
467 | auto folderList = QSharedPointer<QVector<Folder>>::create(); | 491 | auto folderList = QSharedPointer<QVector<Folder>>::create(); |
468 | return imap->fetchFolders([folderList](const Folder &folder) { | 492 | return imap->fetchFolders([folderList](const Folder &folder) { |
469 | if (!folder.noselect) { | 493 | if (!folder.noselect && folder.subscribed) { |
470 | *folderList << folder; | 494 | *folderList << folder; |
471 | } | 495 | } |
472 | }) | 496 | }) |
@@ -480,12 +504,16 @@ public: | |||
480 | KAsync::Error getError(const KAsync::Error &error) | 504 | KAsync::Error getError(const KAsync::Error &error) |
481 | { | 505 | { |
482 | if (error) { | 506 | if (error) { |
483 | if (error.errorCode == Imap::CouldNotConnectError) { | 507 | switch(error.errorCode) { |
484 | return {ApplicationDomain::ConnectionError, error.errorMessage}; | 508 | case Imap::CouldNotConnectError: |
485 | } else if (error.errorCode == Imap::SslHandshakeError) { | 509 | return {ApplicationDomain::ConnectionError, error.errorMessage}; |
486 | return {ApplicationDomain::LoginError, error.errorMessage}; | 510 | case Imap::SslHandshakeError: |
511 | return {ApplicationDomain::LoginError, error.errorMessage}; | ||
512 | case Imap::HostNotFoundError: | ||
513 | return {ApplicationDomain::NoServerError, error.errorMessage}; | ||
514 | default: | ||
515 | return {ApplicationDomain::UnknownError, error.errorMessage}; | ||
487 | } | 516 | } |
488 | return {ApplicationDomain::UnknownError, error.errorMessage}; | ||
489 | } | 517 | } |
490 | return {}; | 518 | return {}; |
491 | } | 519 | } |
@@ -539,11 +567,12 @@ public: | |||
539 | } | 567 | } |
540 | SinkLog() << "Fetching messages: " << toFetch << folderRemoteId; | 568 | SinkLog() << "Fetching messages: " << toFetch << folderRemoteId; |
541 | bool headersOnly = false; | 569 | bool headersOnly = false; |
570 | const auto folderLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, folderRemoteId); | ||
542 | return imap->fetchMessages(Folder{folderRemoteId}, toFetch, headersOnly, [=](const Message &m) { | 571 | return imap->fetchMessages(Folder{folderRemoteId}, toFetch, headersOnly, [=](const Message &m) { |
543 | synchronizeMails(folderRemoteId, m); | 572 | synchronizeMails(folderRemoteId, folderLocalId, m); |
544 | }, | 573 | }, |
545 | [=](int progress, int total) { | 574 | [=](int progress, int total) { |
546 | reportProgress(progress, total); | 575 | reportProgress(progress, total, QByteArrayList{} << folderLocalId); |
547 | //commit every 100 messages | 576 | //commit every 100 messages |
548 | if ((progress % 100) == 0) { | 577 | if ((progress % 100) == 0) { |
549 | commit(); | 578 | commit(); |
@@ -554,10 +583,7 @@ public: | |||
554 | bool syncHeaders = query.hasFilter<ApplicationDomain::Mail::Folder>(); | 583 | bool syncHeaders = query.hasFilter<ApplicationDomain::Mail::Folder>(); |
555 | //FIXME If we were able to to flush in between we could just query the local store for the folder list. | 584 | //FIXME If we were able to to flush in between we could just query the local store for the folder list. |
556 | return getFolderList(imap, query) | 585 | return getFolderList(imap, query) |
557 | .then([=] (const QVector<Folder> &folders) { | 586 | .serialEach([=](const Folder &folder) { |
558 | //Synchronize folders | ||
559 | return KAsync::value(folders) | ||
560 | .serialEach<void>([=](const Folder &folder) { | ||
561 | SinkLog() << "Syncing folder " << folder.path(); | 587 | SinkLog() << "Syncing folder " << folder.path(); |
562 | //Emit notification that the folder is being synced. | 588 | //Emit notification that the folder is being synced. |
563 | //The synchronizer can't do that because it has no concept of the folder filter on a mail sync scope meaning that the folder is being synchronized. | 589 | //The synchronizer can't do that because it has no concept of the folder filter on a mail sync scope meaning that the folder is being synchronized. |
@@ -572,7 +598,6 @@ public: | |||
572 | SinkWarning() << "Failed to sync folder: " << folder.path() << "Error: " << error.errorMessage; | 598 | SinkWarning() << "Failed to sync folder: " << folder.path() << "Error: " << error.errorMessage; |
573 | }); | 599 | }); |
574 | }); | 600 | }); |
575 | }); | ||
576 | } | 601 | } |
577 | }) | 602 | }) |
578 | .then([=] (const KAsync::Error &error) { | 603 | .then([=] (const KAsync::Error &error) { |
@@ -582,6 +607,15 @@ public: | |||
582 | } | 607 | } |
583 | return KAsync::error<void>("Nothing to do"); | 608 | return KAsync::error<void>("Nothing to do"); |
584 | } | 609 | } |
610 | static QByteArray ensureCRLF(const QByteArray &data) { | ||
611 | auto index = data.indexOf('\n'); | ||
612 | if (index > 0 && data.at(index - 1) == '\r') { //First line is LF-only terminated | ||
613 | //Convert back and forth in case there's a mix. We don't want to expand CRLF into CRCRLF. | ||
614 | return KMime::LFtoCRLF(KMime::CRLFtoLF(data)); | ||
615 | } else { | ||
616 | return data; | ||
617 | } | ||
618 | } | ||
585 | 619 | ||
586 | KAsync::Job<QByteArray> replay(const ApplicationDomain::Mail &mail, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE | 620 | KAsync::Job<QByteArray> replay(const ApplicationDomain::Mail &mail, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE |
587 | { | 621 | { |
@@ -589,10 +623,10 @@ public: | |||
589 | auto login = imap->login(mUser, mPassword); | 623 | auto login = imap->login(mUser, mPassword); |
590 | KAsync::Job<QByteArray> job = KAsync::null<QByteArray>(); | 624 | KAsync::Job<QByteArray> job = KAsync::null<QByteArray>(); |
591 | if (operation == Sink::Operation_Creation) { | 625 | if (operation == Sink::Operation_Creation) { |
592 | QString mailbox = syncStore().resolveLocalId(ENTITY_TYPE_FOLDER, mail.getFolder()); | 626 | const QString mailbox = syncStore().resolveLocalId(ENTITY_TYPE_FOLDER, mail.getFolder()); |
593 | QByteArray content = KMime::LFtoCRLF(mail.getMimeMessage()); | 627 | const auto content = ensureCRLF(mail.getMimeMessage()); |
594 | auto flags = getFlags(mail); | 628 | const auto flags = getFlags(mail); |
595 | QDateTime internalDate = mail.getDate(); | 629 | const QDateTime internalDate = mail.getDate(); |
596 | job = login.then(imap->append(mailbox, content, flags, internalDate)) | 630 | job = login.then(imap->append(mailbox, content, flags, internalDate)) |
597 | .addToContext(imap) | 631 | .addToContext(imap) |
598 | .then([mail](qint64 uid) { | 632 | .then([mail](qint64 uid) { |
@@ -623,11 +657,11 @@ public: | |||
623 | const bool messageMoved = changedProperties.contains(ApplicationDomain::Mail::Folder::name); | 657 | const bool messageMoved = changedProperties.contains(ApplicationDomain::Mail::Folder::name); |
624 | const bool messageChanged = changedProperties.contains(ApplicationDomain::Mail::MimeMessage::name); | 658 | const bool messageChanged = changedProperties.contains(ApplicationDomain::Mail::MimeMessage::name); |
625 | if (messageChanged || messageMoved) { | 659 | if (messageChanged || messageMoved) { |
626 | SinkTrace() << "Replacing message."; | ||
627 | const auto folderId = folderIdFromMailRid(oldRemoteId); | 660 | const auto folderId = folderIdFromMailRid(oldRemoteId); |
628 | const QString oldMailbox = syncStore().resolveLocalId(ENTITY_TYPE_FOLDER, folderId); | 661 | const QString oldMailbox = syncStore().resolveLocalId(ENTITY_TYPE_FOLDER, folderId); |
629 | QByteArray content = KMime::LFtoCRLF(mail.getMimeMessage()); | 662 | const auto content = ensureCRLF(mail.getMimeMessage()); |
630 | QDateTime internalDate = mail.getDate(); | 663 | const QDateTime internalDate = mail.getDate(); |
664 | SinkTrace() << "Replacing message. Old mailbox: " << oldMailbox << "New mailbox: " << mailbox << "Flags: " << flags << "Content: " << content; | ||
631 | KIMAP2::ImapSet set; | 665 | KIMAP2::ImapSet set; |
632 | set.add(uid); | 666 | set.add(uid); |
633 | job = login.then(imap->append(mailbox, content, flags, internalDate)) | 667 | job = login.then(imap->append(mailbox, content, flags, internalDate)) |
@@ -655,7 +689,7 @@ public: | |||
655 | if (error) { | 689 | if (error) { |
656 | SinkWarning() << "Error during changereplay: " << error.errorMessage; | 690 | SinkWarning() << "Error during changereplay: " << error.errorMessage; |
657 | return imap->logout() | 691 | return imap->logout() |
658 | .then(KAsync::error<QByteArray>(error)); | 692 | .then(KAsync::error<QByteArray>(getError(error))); |
659 | } | 693 | } |
660 | return imap->logout() | 694 | return imap->logout() |
661 | .then(KAsync::value(remoteId)); | 695 | .then(KAsync::value(remoteId)); |
@@ -664,6 +698,12 @@ public: | |||
664 | 698 | ||
665 | KAsync::Job<QByteArray> replay(const ApplicationDomain::Folder &folder, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE | 699 | KAsync::Job<QByteArray> replay(const ApplicationDomain::Folder &folder, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE |
666 | { | 700 | { |
701 | if (operation != Sink::Operation_Creation) { | ||
702 | if(oldRemoteId.isEmpty()) { | ||
703 | Q_ASSERT(false); | ||
704 | return KAsync::error<QByteArray>("Tried to replay modification without old remoteId."); | ||
705 | } | ||
706 | } | ||
667 | auto imap = QSharedPointer<ImapServerProxy>::create(mServer, mPort, &mSessionCache); | 707 | auto imap = QSharedPointer<ImapServerProxy>::create(mServer, mPort, &mSessionCache); |
668 | auto login = imap->login(mUser, mPassword); | 708 | auto login = imap->login(mUser, mPassword); |
669 | if (operation == Sink::Operation_Creation) { | 709 | if (operation == Sink::Operation_Creation) { |
@@ -793,6 +833,10 @@ protected: | |||
793 | .then(imap->select(folderRemoteId)) | 833 | .then(imap->select(folderRemoteId)) |
794 | .then([](Imap::SelectResult){}) | 834 | .then([](Imap::SelectResult){}) |
795 | .then(imap->fetch(set, scope, [imap, messageByUid](const Imap::Message &message) { | 835 | .then(imap->fetch(set, scope, [imap, messageByUid](const Imap::Message &message) { |
836 | //We avoid parsing normally, so we have to do it explicitly here | ||
837 | if (message.msg) { | ||
838 | message.msg->parse(); | ||
839 | } | ||
796 | messageByUid->insert(message.uid, message); | 840 | messageByUid->insert(message.uid, message); |
797 | })); | 841 | })); |
798 | 842 | ||
diff --git a/examples/imapresource/imapserverproxy.cpp b/examples/imapresource/imapserverproxy.cpp index 0cc43b8..538105c 100644 --- a/examples/imapresource/imapserverproxy.cpp +++ b/examples/imapresource/imapserverproxy.cpp | |||
@@ -37,8 +37,6 @@ | |||
37 | #include "log.h" | 37 | #include "log.h" |
38 | #include "test.h" | 38 | #include "test.h" |
39 | 39 | ||
40 | SINK_DEBUG_AREA("imapserverproxy") | ||
41 | |||
42 | using namespace Imap; | 40 | using namespace Imap; |
43 | 41 | ||
44 | const char* Imap::Flags::Seen = "\\Seen"; | 42 | const char* Imap::Flags::Seen = "\\Seen"; |
@@ -57,6 +55,7 @@ const char* Imap::FolderFlags::Trash = "\\Trash"; | |||
57 | const char* Imap::FolderFlags::Archive = "\\Archive"; | 55 | const char* Imap::FolderFlags::Archive = "\\Archive"; |
58 | const char* Imap::FolderFlags::Junk = "\\Junk"; | 56 | const char* Imap::FolderFlags::Junk = "\\Junk"; |
59 | const char* Imap::FolderFlags::Flagged = "\\Flagged"; | 57 | const char* Imap::FolderFlags::Flagged = "\\Flagged"; |
58 | const char* Imap::FolderFlags::Drafts = "\\Drafts"; | ||
60 | 59 | ||
61 | const char* Imap::Capabilities::Namespace = "NAMESPACE"; | 60 | const char* Imap::Capabilities::Namespace = "NAMESPACE"; |
62 | const char* Imap::Capabilities::Uidplus = "UIDPLUS"; | 61 | const char* Imap::Capabilities::Uidplus = "UIDPLUS"; |
@@ -98,17 +97,25 @@ static KAsync::Job<void> runJob(KJob *job) | |||
98 | }); | 97 | }); |
99 | } | 98 | } |
100 | 99 | ||
101 | ImapServerProxy::ImapServerProxy(const QString &serverUrl, int port, SessionCache *sessionCache) : mSession(new KIMAP2::Session(serverUrl, qint16(port))), mSessionCache(sessionCache) | 100 | KIMAP2::Session *createNewSession(const QString &serverUrl, int port) |
102 | { | 101 | { |
103 | QObject::connect(mSession, &KIMAP2::Session::sslErrors, [this](const QList<QSslError> &errors) { | 102 | auto newSession = new KIMAP2::Session(serverUrl, qint16(port)); |
103 | if (Sink::Test::testModeEnabled()) { | ||
104 | newSession->setTimeout(1); | ||
105 | } else { | ||
106 | newSession->setTimeout(40); | ||
107 | } | ||
108 | QObject::connect(newSession, &KIMAP2::Session::sslErrors, [=](const QList<QSslError> &errors) { | ||
104 | SinkLog() << "Received ssl error: " << errors; | 109 | SinkLog() << "Received ssl error: " << errors; |
105 | mSession->ignoreErrors(errors); | 110 | newSession->ignoreErrors(errors); |
106 | }); | 111 | }); |
112 | return newSession; | ||
113 | } | ||
107 | 114 | ||
108 | if (Sink::Test::testModeEnabled()) { | 115 | ImapServerProxy::ImapServerProxy(const QString &serverUrl, int port, SessionCache *sessionCache) : mSessionCache(sessionCache), mSession(nullptr) |
109 | mSession->setTimeout(1); | 116 | { |
110 | } else { | 117 | if (!mSessionCache || mSessionCache->isEmpty()) { |
111 | mSession->setTimeout(40); | 118 | mSession = createNewSession(serverUrl, port); |
112 | } | 119 | } |
113 | } | 120 | } |
114 | 121 | ||
@@ -161,12 +168,16 @@ KAsync::Job<void> ImapServerProxy::login(const QString &username, const QString | |||
161 | // SinkTrace() << "Found user namespaces: " << mNamespaces.user; | 168 | // SinkTrace() << "Found user namespaces: " << mNamespaces.user; |
162 | }).then([=] (const KAsync::Error &error) { | 169 | }).then([=] (const KAsync::Error &error) { |
163 | if (error) { | 170 | if (error) { |
164 | if (error.errorCode == KIMAP2::LoginJob::ErrorCode::ERR_COULD_NOT_CONNECT) { | 171 | switch (error.errorCode) { |
172 | case KIMAP2::LoginJob::ErrorCode::ERR_HOST_NOT_FOUND: | ||
173 | return KAsync::error(HostNotFoundError, "Host not found: " + error.errorMessage); | ||
174 | case KIMAP2::LoginJob::ErrorCode::ERR_COULD_NOT_CONNECT: | ||
165 | return KAsync::error(CouldNotConnectError, "Failed to connect: " + error.errorMessage); | 175 | return KAsync::error(CouldNotConnectError, "Failed to connect: " + error.errorMessage); |
166 | } else if (error.errorCode == KIMAP2::LoginJob::ErrorCode::ERR_SSL_HANDSHAKE_FAILED) { | 176 | case KIMAP2::LoginJob::ErrorCode::ERR_SSL_HANDSHAKE_FAILED: |
167 | return KAsync::error(SslHandshakeError, "Ssl handshake failed: " + error.errorMessage); | 177 | return KAsync::error(SslHandshakeError, "Ssl handshake failed: " + error.errorMessage); |
178 | default: | ||
179 | return KAsync::error(error); | ||
168 | } | 180 | } |
169 | return KAsync::error(error); | ||
170 | } | 181 | } |
171 | return KAsync::null(); | 182 | return KAsync::null(); |
172 | }); | 183 | }); |
@@ -188,6 +199,12 @@ KAsync::Job<void> ImapServerProxy::logout() | |||
188 | } | 199 | } |
189 | } | 200 | } |
190 | 201 | ||
202 | bool ImapServerProxy::isGmail() const | ||
203 | { | ||
204 | //Magic capability that only gmail has | ||
205 | return mCapabilities.contains("X-GM-EXT-1"); | ||
206 | } | ||
207 | |||
191 | KAsync::Job<SelectResult> ImapServerProxy::select(const QString &mailbox) | 208 | KAsync::Job<SelectResult> ImapServerProxy::select(const QString &mailbox) |
192 | { | 209 | { |
193 | auto select = new KIMAP2::SelectJob(mSession); | 210 | auto select = new KIMAP2::SelectJob(mSession); |
@@ -297,6 +314,7 @@ KAsync::Job<void> ImapServerProxy::fetch(const KIMAP2::ImapSet &set, KIMAP2::Fet | |||
297 | fetch->setSequenceSet(set); | 314 | fetch->setSequenceSet(set); |
298 | fetch->setUidBased(true); | 315 | fetch->setUidBased(true); |
299 | fetch->setScope(scope); | 316 | fetch->setScope(scope); |
317 | fetch->setAvoidParsing(true); | ||
300 | QObject::connect(fetch, &KIMAP2::FetchJob::resultReceived, callback); | 318 | QObject::connect(fetch, &KIMAP2::FetchJob::resultReceived, callback); |
301 | return runJob(fetch); | 319 | return runJob(fetch); |
302 | } | 320 | } |
@@ -437,18 +455,60 @@ QString ImapServerProxy::getNamespace(const QString &name) | |||
437 | return ns.name; | 455 | return ns.name; |
438 | } | 456 | } |
439 | 457 | ||
458 | static bool caseInsensitiveContains(const QByteArray &f, const QByteArrayList &list) { | ||
459 | return list.contains(f) || list.contains(f.toLower()); | ||
460 | } | ||
461 | |||
462 | bool Imap::flagsContain(const QByteArray &f, const QByteArrayList &flags) | ||
463 | { | ||
464 | return caseInsensitiveContains(f, flags); | ||
465 | } | ||
466 | |||
467 | static void reportFolder(const Folder &f, QSharedPointer<QSet<QString>> reportedList, std::function<void(const Folder &)> callback) { | ||
468 | if (!reportedList->contains(f.path())) { | ||
469 | reportedList->insert(f.path()); | ||
470 | auto c = f; | ||
471 | c.noselect = true; | ||
472 | callback(c); | ||
473 | if (!f.parentPath().isEmpty()){ | ||
474 | reportFolder(f.parentFolder(), reportedList, callback); | ||
475 | } | ||
476 | } | ||
477 | } | ||
478 | |||
440 | KAsync::Job<void> ImapServerProxy::fetchFolders(std::function<void(const Folder &)> callback) | 479 | KAsync::Job<void> ImapServerProxy::fetchFolders(std::function<void(const Folder &)> callback) |
441 | { | 480 | { |
442 | SinkTrace() << "Fetching folders"; | 481 | SinkTrace() << "Fetching folders"; |
443 | auto subscribedList = QSharedPointer<QSet<QString>>::create() ; | 482 | auto subscribedList = QSharedPointer<QSet<QString>>::create() ; |
483 | auto reportedList = QSharedPointer<QSet<QString>>::create() ; | ||
444 | return list(KIMAP2::ListJob::NoOption, [=](const KIMAP2::MailBoxDescriptor &mailbox, const QList<QByteArray> &){ | 484 | return list(KIMAP2::ListJob::NoOption, [=](const KIMAP2::MailBoxDescriptor &mailbox, const QList<QByteArray> &){ |
445 | *subscribedList << mailbox.name; | 485 | *subscribedList << mailbox.name; |
446 | }).then(list(KIMAP2::ListJob::IncludeUnsubscribed, [=](const KIMAP2::MailBoxDescriptor &mailbox, const QList<QByteArray> &flags) { | 486 | }).then(list(KIMAP2::ListJob::IncludeUnsubscribed, [=](const KIMAP2::MailBoxDescriptor &mailbox, const QList<QByteArray> &flags) { |
447 | bool noselect = flags.contains(QByteArray(FolderFlags::Noselect).toLower()) || flags.contains(QByteArray(FolderFlags::Noselect)); | 487 | bool noselect = caseInsensitiveContains(FolderFlags::Noselect, flags); |
448 | bool subscribed = subscribedList->contains(mailbox.name); | 488 | bool subscribed = subscribedList->contains(mailbox.name); |
489 | if (isGmail()) { | ||
490 | bool inbox = mailbox.name.toLower() == "inbox"; | ||
491 | bool sent = caseInsensitiveContains(FolderFlags::Sent, flags); | ||
492 | bool drafts = caseInsensitiveContains(FolderFlags::Drafts, flags); | ||
493 | bool trash = caseInsensitiveContains(FolderFlags::Trash, flags); | ||
494 | /** | ||
495 | * Because gmail duplicates messages all over the place we only support a few selected folders for now that should be mostly exclusive. | ||
496 | */ | ||
497 | if (!(inbox || sent || drafts || trash)) { | ||
498 | return; | ||
499 | } | ||
500 | } | ||
449 | SinkLog() << "Found mailbox: " << mailbox.name << flags << FolderFlags::Noselect << noselect << " sub: " << subscribed; | 501 | SinkLog() << "Found mailbox: " << mailbox.name << flags << FolderFlags::Noselect << noselect << " sub: " << subscribed; |
450 | auto ns = getNamespace(mailbox.name); | 502 | auto ns = getNamespace(mailbox.name); |
451 | callback(Folder{mailbox.name, ns, mailbox.separator, noselect, subscribed, flags}); | 503 | auto folder = Folder{mailbox.name, ns, mailbox.separator, noselect, subscribed, flags}; |
504 | |||
505 | //call callback for parents if that didn't already happen. | ||
506 | //This is necessary because we can have missing bits in the hierarchy in IMAP, but this will not work in sink because we'd end up with an incomplete tree. | ||
507 | if (!folder.parentPath().isEmpty() && !reportedList->contains(folder.parentPath())) { | ||
508 | reportFolder(folder.parentFolder(), reportedList, callback); | ||
509 | } | ||
510 | reportedList->insert(folder.path()); | ||
511 | callback(folder); | ||
452 | })); | 512 | })); |
453 | } | 513 | } |
454 | 514 | ||
diff --git a/examples/imapresource/imapserverproxy.h b/examples/imapresource/imapserverproxy.h index 872f032..82f4f58 100644 --- a/examples/imapresource/imapserverproxy.h +++ b/examples/imapresource/imapserverproxy.h | |||
@@ -31,6 +31,7 @@ namespace Imap { | |||
31 | 31 | ||
32 | enum ErrorCode { | 32 | enum ErrorCode { |
33 | NoError, | 33 | NoError, |
34 | HostNotFoundError, | ||
34 | CouldNotConnectError, | 35 | CouldNotConnectError, |
35 | SslHandshakeError | 36 | SslHandshakeError |
36 | }; | 37 | }; |
@@ -60,6 +61,7 @@ namespace FolderFlags | |||
60 | extern const char* Junk; | 61 | extern const char* Junk; |
61 | extern const char* Flagged; | 62 | extern const char* Flagged; |
62 | extern const char* All; | 63 | extern const char* All; |
64 | extern const char* Drafts; | ||
63 | } | 65 | } |
64 | 66 | ||
65 | namespace Capabilities | 67 | namespace Capabilities |
@@ -78,6 +80,8 @@ struct Message { | |||
78 | bool fullPayload; | 80 | bool fullPayload; |
79 | }; | 81 | }; |
80 | 82 | ||
83 | bool flagsContain(const QByteArray &f, const QByteArrayList &flags); | ||
84 | |||
81 | struct Folder { | 85 | struct Folder { |
82 | Folder() = default; | 86 | Folder() = default; |
83 | Folder(const QString &path, const QString &ns, const QChar &separator, bool noselect_, bool subscribed_, const QByteArrayList &flags_) | 87 | Folder(const QString &path, const QString &ns, const QChar &separator, bool noselect_, bool subscribed_, const QByteArrayList &flags_) |
@@ -114,6 +118,15 @@ struct Folder { | |||
114 | return parentPath; | 118 | return parentPath; |
115 | } | 119 | } |
116 | 120 | ||
121 | Folder parentFolder() const | ||
122 | { | ||
123 | Folder parent; | ||
124 | parent.mPath = parentPath(); | ||
125 | parent.mNamespace = mNamespace; | ||
126 | parent.mSeparator = mSeparator; | ||
127 | return parent; | ||
128 | } | ||
129 | |||
117 | QString name() const | 130 | QString name() const |
118 | { | 131 | { |
119 | auto pathParts = mPath.split(mSeparator); | 132 | auto pathParts = mPath.split(mSeparator); |
@@ -218,18 +231,18 @@ public: | |||
218 | return session; | 231 | return session; |
219 | } | 232 | } |
220 | } | 233 | } |
221 | return CachedSession{}; | 234 | return {}; |
235 | } | ||
236 | |||
237 | bool isEmpty() const | ||
238 | { | ||
239 | return mSessions.isEmpty(); | ||
222 | } | 240 | } |
223 | private: | 241 | private: |
224 | QList<CachedSession> mSessions; | 242 | QList<CachedSession> mSessions; |
225 | }; | 243 | }; |
226 | 244 | ||
227 | class ImapServerProxy { | 245 | class ImapServerProxy { |
228 | KIMAP2::Session *mSession; | ||
229 | QStringList mCapabilities; | ||
230 | Namespaces mNamespaces; | ||
231 | |||
232 | |||
233 | public: | 246 | public: |
234 | ImapServerProxy(const QString &serverUrl, int port, SessionCache *sessionCache = nullptr); | 247 | ImapServerProxy(const QString &serverUrl, int port, SessionCache *sessionCache = nullptr); |
235 | 248 | ||
@@ -279,9 +292,14 @@ public: | |||
279 | KAsync::Job<QVector<qint64>> fetchUids(const Folder &folder); | 292 | KAsync::Job<QVector<qint64>> fetchUids(const Folder &folder); |
280 | 293 | ||
281 | private: | 294 | private: |
295 | bool isGmail() const; | ||
296 | |||
282 | QString getNamespace(const QString &name); | 297 | QString getNamespace(const QString &name); |
283 | QObject mGuard; | 298 | QObject mGuard; |
284 | SessionCache *mSessionCache; | 299 | SessionCache *mSessionCache; |
300 | KIMAP2::Session *mSession; | ||
301 | QStringList mCapabilities; | ||
302 | Namespaces mNamespaces; | ||
285 | }; | 303 | }; |
286 | 304 | ||
287 | } | 305 | } |
diff --git a/examples/imapresource/tests/imapmailsyncbenchmark.cpp b/examples/imapresource/tests/imapmailsyncbenchmark.cpp index a53c148..814e325 100644 --- a/examples/imapresource/tests/imapmailsyncbenchmark.cpp +++ b/examples/imapresource/tests/imapmailsyncbenchmark.cpp | |||
@@ -31,8 +31,6 @@ | |||
31 | using namespace Sink; | 31 | using namespace Sink; |
32 | using namespace Sink::ApplicationDomain; | 32 | using namespace Sink::ApplicationDomain; |
33 | 33 | ||
34 | SINK_DEBUG_AREA("ImapMailSyncBenchmark") | ||
35 | |||
36 | /** | 34 | /** |
37 | * Test of complete system using the imap resource. | 35 | * Test of complete system using the imap resource. |
38 | * | 36 | * |
diff --git a/examples/imapresource/tests/imapserverproxytest.cpp b/examples/imapresource/tests/imapserverproxytest.cpp index 476066d..271b3d9 100644 --- a/examples/imapresource/tests/imapserverproxytest.cpp +++ b/examples/imapresource/tests/imapserverproxytest.cpp | |||
@@ -12,8 +12,6 @@ | |||
12 | 12 | ||
13 | using namespace Imap; | 13 | using namespace Imap; |
14 | 14 | ||
15 | // SINK_DEBUG_AREA("imapserverproxytest") | ||
16 | |||
17 | /** | 15 | /** |
18 | */ | 16 | */ |
19 | class ImapServerProxyTest : public QObject | 17 | class ImapServerProxyTest : public QObject |
diff --git a/examples/imapresource/tests/populatemailbox.sh b/examples/imapresource/tests/populatemailbox.sh index a435df7..800e2e7 100644 --- a/examples/imapresource/tests/populatemailbox.sh +++ b/examples/imapresource/tests/populatemailbox.sh | |||
@@ -1,12 +1,23 @@ | |||
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | 2 | ||
3 | sudo echo "sam user.doe.* cyrus c" | cyradm --auth PLAIN -u cyrus -w admin localhost | 3 | sudo echo "sam user.doe.* cyrus c" | cyradm --auth PLAIN -u cyrus -w admin localhost |
4 | #Delete all mailboxes | ||
4 | sudo echo "dm user.doe.*" | cyradm --auth PLAIN -u cyrus -w admin localhost | 5 | sudo echo "dm user.doe.*" | cyradm --auth PLAIN -u cyrus -w admin localhost |
6 | #Create mailboxes | ||
5 | sudo echo "cm user.doe.test" | cyradm --auth PLAIN -u cyrus -w admin localhost | 7 | sudo echo "cm user.doe.test" | cyradm --auth PLAIN -u cyrus -w admin localhost |
6 | sudo echo "cm user.doe.Drafts" | cyradm --auth PLAIN -u cyrus -w admin localhost | 8 | sudo echo "cm user.doe.Drafts" | cyradm --auth PLAIN -u cyrus -w admin localhost |
7 | sudo echo "cm user.doe.Trash" | cyradm --auth PLAIN -u cyrus -w admin localhost | 9 | sudo echo "cm user.doe.Trash" | cyradm --auth PLAIN -u cyrus -w admin localhost |
10 | |||
11 | #Set acls so we can create in INBOX | ||
8 | sudo echo "sam user.doe cyrus c" | cyradm --auth PLAIN -u cyrus -w admin localhost | 12 | sudo echo "sam user.doe cyrus c" | cyradm --auth PLAIN -u cyrus -w admin localhost |
9 | 13 | ||
14 | #Subscribe to mailboxes | ||
15 | sudo echo "sub INBOX" | cyradm --auth PLAIN -u doe -w doe localhost | ||
16 | sudo echo "sub INBOX.test" | cyradm --auth PLAIN -u doe -w doe localhost | ||
17 | sudo echo "sub INBOX.Drafts" | cyradm --auth PLAIN -u doe -w doe localhost | ||
18 | sudo echo "sub INBOX.Trash" | cyradm --auth PLAIN -u doe -w doe localhost | ||
19 | |||
20 | #Create a bunch of test messages in the test folder | ||
10 | # for i in `seq 1 5000`; | 21 | # for i in `seq 1 5000`; |
11 | # do | 22 | # do |
12 | # # sudo cp /work/source/Sink/examples/imapresource/tests/data/1365777830.R28.localhost.localdomain\:2\,S /var/spool/imap/d/user/doe/test/$i. | 23 | # # sudo cp /work/source/Sink/examples/imapresource/tests/data/1365777830.R28.localhost.localdomain\:2\,S /var/spool/imap/d/user/doe/test/$i. |
diff --git a/examples/imapresource/tests/resetmailbox.sh b/examples/imapresource/tests/resetmailbox.sh index 6ed198e..63d3478 100644 --- a/examples/imapresource/tests/resetmailbox.sh +++ b/examples/imapresource/tests/resetmailbox.sh | |||
@@ -3,8 +3,11 @@ | |||
3 | sudo echo "sam user.doe.* cyrus c" | cyradm --auth PLAIN -u cyrus -w admin localhost | 3 | sudo echo "sam user.doe.* cyrus c" | cyradm --auth PLAIN -u cyrus -w admin localhost |
4 | sudo echo "dm user.doe.*" | cyradm --auth PLAIN -u cyrus -w admin localhost | 4 | sudo echo "dm user.doe.*" | cyradm --auth PLAIN -u cyrus -w admin localhost |
5 | sudo echo "cm user.doe.test" | cyradm --auth PLAIN -u cyrus -w admin localhost | 5 | sudo echo "cm user.doe.test" | cyradm --auth PLAIN -u cyrus -w admin localhost |
6 | sudo echo "subscribe INBOX.test" | cyradm --auth PLAIN -u doe -w doe localhost | ||
6 | sudo echo "cm user.doe.Drafts" | cyradm --auth PLAIN -u cyrus -w admin localhost | 7 | sudo echo "cm user.doe.Drafts" | cyradm --auth PLAIN -u cyrus -w admin localhost |
8 | sudo echo "subscribe INBOX.Drafts" | cyradm --auth PLAIN -u doe -w doe localhost | ||
7 | sudo echo "cm user.doe.Trash" | cyradm --auth PLAIN -u cyrus -w admin localhost | 9 | sudo echo "cm user.doe.Trash" | cyradm --auth PLAIN -u cyrus -w admin localhost |
10 | sudo echo "subscribe INBOX.Trash" | cyradm --auth PLAIN -u doe -w doe localhost | ||
8 | sudo echo "sam user.doe cyrus c" | cyradm --auth PLAIN -u cyrus -w admin localhost | 11 | sudo echo "sam user.doe cyrus c" | cyradm --auth PLAIN -u cyrus -w admin localhost |
9 | sudo cp /work/source/Sink/examples/imapresource/tests/data/1365777830.R28.localhost.localdomain\:2\,S /var/spool/imap/d/user/doe/test/1. | 12 | sudo cp /work/source/Sink/examples/imapresource/tests/data/1365777830.R28.localhost.localdomain\:2\,S /var/spool/imap/d/user/doe/test/1. |
10 | sudo chown cyrus:mail /var/spool/imap/d/user/doe/test/1. | 13 | sudo chown cyrus:mail /var/spool/imap/d/user/doe/test/1. |
diff --git a/examples/maildirresource/maildirresource.cpp b/examples/maildirresource/maildirresource.cpp index 40bab37..b406f63 100644 --- a/examples/maildirresource/maildirresource.cpp +++ b/examples/maildirresource/maildirresource.cpp | |||
@@ -43,8 +43,6 @@ | |||
43 | #define ENTITY_TYPE_MAIL "mail" | 43 | #define ENTITY_TYPE_MAIL "mail" |
44 | #define ENTITY_TYPE_FOLDER "folder" | 44 | #define ENTITY_TYPE_FOLDER "folder" |
45 | 45 | ||
46 | SINK_DEBUG_AREA("maildirresource") | ||
47 | |||
48 | using namespace Sink; | 46 | using namespace Sink; |
49 | 47 | ||
50 | static QString getFilePathFromMimeMessagePath(const QString &mimeMessagePath) | 48 | static QString getFilePathFromMimeMessagePath(const QString &mimeMessagePath) |
@@ -555,18 +553,20 @@ MaildirResource::MaildirResource(const Sink::ResourceContext &resourceContext) | |||
555 | setupPreprocessors(ENTITY_TYPE_FOLDER, QVector<Sink::Preprocessor*>() << new FolderPreprocessor(mMaildirPath)); | 553 | setupPreprocessors(ENTITY_TYPE_FOLDER, QVector<Sink::Preprocessor*>() << new FolderPreprocessor(mMaildirPath)); |
556 | 554 | ||
557 | KPIM::Maildir dir(mMaildirPath, true); | 555 | KPIM::Maildir dir(mMaildirPath, true); |
558 | SinkTrace() << "Started maildir resource for maildir: " << mMaildirPath; | 556 | if (dir.isValid(false)) { |
559 | { | 557 | { |
560 | auto draftsFolder = dir.addSubFolder("Drafts"); | 558 | auto draftsFolder = dir.addSubFolder("Drafts"); |
561 | auto remoteId = synchronizer->createFolder(draftsFolder, "folder", QByteArrayList() << "drafts"); | 559 | auto remoteId = synchronizer->createFolder(draftsFolder, "folder", QByteArrayList() << "drafts"); |
562 | auto draftsFolderLocalId = synchronizer->syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, remoteId); | 560 | auto draftsFolderLocalId = synchronizer->syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, remoteId); |
563 | } | 561 | } |
564 | { | 562 | { |
565 | auto trashFolder = dir.addSubFolder("Trash"); | 563 | auto trashFolder = dir.addSubFolder("Trash"); |
566 | auto remoteId = synchronizer->createFolder(trashFolder, "folder", QByteArrayList() << "trash"); | 564 | auto remoteId = synchronizer->createFolder(trashFolder, "folder", QByteArrayList() << "trash"); |
567 | auto trashFolderLocalId = synchronizer->syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, remoteId); | 565 | auto trashFolderLocalId = synchronizer->syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, remoteId); |
566 | } | ||
567 | synchronizer->commit(); | ||
568 | } | 568 | } |
569 | synchronizer->commit(); | 569 | SinkTrace() << "Started maildir resource for maildir: " << mMaildirPath; |
570 | } | 570 | } |
571 | 571 | ||
572 | 572 | ||
diff --git a/examples/mailtransportresource/mailtransport.cpp b/examples/mailtransportresource/mailtransport.cpp index 84c1556..afe0257 100644 --- a/examples/mailtransportresource/mailtransport.cpp +++ b/examples/mailtransportresource/mailtransport.cpp | |||
@@ -23,8 +23,6 @@ | |||
23 | #include <QDebug> | 23 | #include <QDebug> |
24 | #include <common/log.h> | 24 | #include <common/log.h> |
25 | 25 | ||
26 | SINK_DEBUG_AREA("mailtransport") | ||
27 | |||
28 | extern "C" { | 26 | extern "C" { |
29 | 27 | ||
30 | #include <stdio.h> | 28 | #include <stdio.h> |
diff --git a/examples/mailtransportresource/mailtransportresource.cpp b/examples/mailtransportresource/mailtransportresource.cpp index c73219f..3d6f8e4 100644 --- a/examples/mailtransportresource/mailtransportresource.cpp +++ b/examples/mailtransportresource/mailtransportresource.cpp | |||
@@ -39,8 +39,6 @@ | |||
39 | 39 | ||
40 | #define ENTITY_TYPE_MAIL "mail" | 40 | #define ENTITY_TYPE_MAIL "mail" |
41 | 41 | ||
42 | SINK_DEBUG_AREA("mailtransportresource") | ||
43 | |||
44 | using namespace Sink; | 42 | using namespace Sink; |
45 | 43 | ||
46 | class MailtransportPreprocessor : public Sink::Preprocessor | 44 | class MailtransportPreprocessor : public Sink::Preprocessor |
diff --git a/examples/mailtransportresource/tests/mailtransporttest.cpp b/examples/mailtransportresource/tests/mailtransporttest.cpp index e4cc447..2a831ed 100644 --- a/examples/mailtransportresource/tests/mailtransporttest.cpp +++ b/examples/mailtransportresource/tests/mailtransporttest.cpp | |||
@@ -64,7 +64,7 @@ private slots: | |||
64 | message->assemble(); | 64 | message->assemble(); |
65 | 65 | ||
66 | auto mail = ApplicationDomain::Mail::create(mResourceInstanceIdentifier); | 66 | auto mail = ApplicationDomain::Mail::create(mResourceInstanceIdentifier); |
67 | mail.setMimeMessage(message->encodedContent()); | 67 | mail.setMimeMessage(message->encodedContent(true)); |
68 | 68 | ||
69 | VERIFYEXEC(Store::create(mail)); | 69 | VERIFYEXEC(Store::create(mail)); |
70 | VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); | 70 | VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); |
@@ -92,7 +92,7 @@ private slots: | |||
92 | message->assemble(); | 92 | message->assemble(); |
93 | 93 | ||
94 | auto mail = ApplicationDomain::Mail::create(mResourceInstanceIdentifier); | 94 | auto mail = ApplicationDomain::Mail::create(mResourceInstanceIdentifier); |
95 | mail.setMimeMessage(message->encodedContent()); | 95 | mail.setMimeMessage(message->encodedContent(true)); |
96 | 96 | ||
97 | VERIFYEXEC(Store::create(mail)); | 97 | VERIFYEXEC(Store::create(mail)); |
98 | VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); | 98 | VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); |
diff --git a/hawd_defs/mail_query_threadleader b/hawd_defs/mail_query_threadleader new file mode 100644 index 0000000..c555997 --- /dev/null +++ b/hawd_defs/mail_query_threadleader | |||
@@ -0,0 +1,8 @@ | |||
1 | { | ||
2 | "name": "Mail Query performance", | ||
3 | "description": "Measures performance of mail queries", | ||
4 | "columns": [ | ||
5 | { "name": "rows", "type": "int" }, | ||
6 | { "name": "queryResultPerMs", "type": "float", "unit": "result/ms" } | ||
7 | ] | ||
8 | } | ||
diff --git a/sinksh/CMakeLists.txt b/sinksh/CMakeLists.txt index 82839ab..bc487c1 100644 --- a/sinksh/CMakeLists.txt +++ b/sinksh/CMakeLists.txt | |||
@@ -18,6 +18,7 @@ set(sink_cli_SRCS | |||
18 | syntax_modules/sink_trace.cpp | 18 | syntax_modules/sink_trace.cpp |
19 | syntax_modules/sink_inspect.cpp | 19 | syntax_modules/sink_inspect.cpp |
20 | syntax_modules/sink_drop.cpp | 20 | syntax_modules/sink_drop.cpp |
21 | syntax_modules/sink_upgrade.cpp | ||
21 | sinksh_utils.cpp | 22 | sinksh_utils.cpp |
22 | repl/repl.cpp | 23 | repl/repl.cpp |
23 | repl/replStates.cpp | 24 | repl/replStates.cpp |
diff --git a/sinksh/state.cpp b/sinksh/state.cpp index 8f4a675..7e04d28 100644 --- a/sinksh/state.cpp +++ b/sinksh/state.cpp | |||
@@ -26,8 +26,6 @@ | |||
26 | 26 | ||
27 | #include "common/log.h" | 27 | #include "common/log.h" |
28 | 28 | ||
29 | SINK_DEBUG_AREA("state") | ||
30 | |||
31 | static bool s_hasEventLoop = false; | 29 | static bool s_hasEventLoop = false; |
32 | 30 | ||
33 | class State::Private | 31 | class State::Private |
diff --git a/sinksh/syntax_modules/sink_upgrade.cpp b/sinksh/syntax_modules/sink_upgrade.cpp new file mode 100644 index 0000000..c399048 --- /dev/null +++ b/sinksh/syntax_modules/sink_upgrade.cpp | |||
@@ -0,0 +1,47 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2017 Christian Mollekopf <mollekopf@kolabsys.com> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the | ||
16 | * Free Software Foundation, Inc., | ||
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
18 | */ | ||
19 | |||
20 | #include <QDebug> | ||
21 | #include <QObject> // tr() | ||
22 | #include <QTimer> | ||
23 | |||
24 | #include "common/store.h" | ||
25 | |||
26 | #include "state.h" | ||
27 | #include "syntaxtree.h" | ||
28 | |||
29 | namespace SinkUpgrade | ||
30 | { | ||
31 | |||
32 | bool upgrade(const QStringList &args, State &state) | ||
33 | { | ||
34 | state.print(QObject::tr("Upgrading...")); | ||
35 | Sink::Store::upgrade().exec().waitForFinished(); | ||
36 | state.printLine(QObject::tr("done")); | ||
37 | return true; | ||
38 | } | ||
39 | |||
40 | Syntax::List syntax() | ||
41 | { | ||
42 | return Syntax::List() << Syntax{"upgrade", QObject::tr("Upgrades your storage to the latest version (be careful!)"), &SinkUpgrade::upgrade, Syntax::NotInteractive}; | ||
43 | } | ||
44 | |||
45 | REGISTER_SYNTAX(SinkUpgrade) | ||
46 | |||
47 | } | ||
diff --git a/synchronizer/CMakeLists.txt b/synchronizer/CMakeLists.txt index ff9ec86..e049d64 100644 --- a/synchronizer/CMakeLists.txt +++ b/synchronizer/CMakeLists.txt | |||
@@ -8,5 +8,5 @@ set(sinksynchronizer_SRCS | |||
8 | 8 | ||
9 | add_executable(${PROJECT_NAME} ${sinksynchronizer_SRCS}) | 9 | add_executable(${PROJECT_NAME} ${sinksynchronizer_SRCS}) |
10 | target_link_libraries(${PROJECT_NAME} sink KAsync ${CMAKE_DL_LIBS}) | 10 | target_link_libraries(${PROJECT_NAME} sink KAsync ${CMAKE_DL_LIBS}) |
11 | qt5_use_modules(${PROJECT_NAME} Network) | 11 | qt5_use_modules(${PROJECT_NAME} Core Network) |
12 | install(TARGETS ${PROJECT_NAME} ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) | 12 | install(TARGETS ${PROJECT_NAME} ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) |
diff --git a/synchronizer/main.cpp b/synchronizer/main.cpp index c66a2fb..3c41c67 100644 --- a/synchronizer/main.cpp +++ b/synchronizer/main.cpp | |||
@@ -39,8 +39,6 @@ | |||
39 | #include "test.h" | 39 | #include "test.h" |
40 | #include "definitions.h" | 40 | #include "definitions.h" |
41 | 41 | ||
42 | SINK_DEBUG_AREA("main") | ||
43 | |||
44 | static Listener *listener = nullptr; | 42 | static Listener *listener = nullptr; |
45 | 43 | ||
46 | //Print a demangled stacktrace | 44 | //Print a demangled stacktrace |
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c77a736..2b3e7b1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt | |||
@@ -16,6 +16,9 @@ add_library(sink_test SHARED testimplementations.cpp getrssusage.cpp mailtest.cp | |||
16 | qt5_use_modules(sink_test Core Test Concurrent) | 16 | qt5_use_modules(sink_test Core Test Concurrent) |
17 | target_link_libraries(sink_test sink libhawd KF5::Mime) | 17 | target_link_libraries(sink_test sink libhawd KF5::Mime) |
18 | 18 | ||
19 | add_executable(dbwriter dbwriter.cpp) | ||
20 | target_link_libraries(dbwriter sink) | ||
21 | |||
19 | include(SinkTest) | 22 | include(SinkTest) |
20 | 23 | ||
21 | manual_tests ( | 24 | manual_tests ( |
diff --git a/tests/dbwriter.cpp b/tests/dbwriter.cpp new file mode 100644 index 0000000..902a607 --- /dev/null +++ b/tests/dbwriter.cpp | |||
@@ -0,0 +1,45 @@ | |||
1 | #include <QByteArrayList> | ||
2 | #include <QDebug> | ||
3 | #include <storage.h> | ||
4 | |||
5 | int main(int argc, char *argv[]) | ||
6 | { | ||
7 | |||
8 | QByteArrayList arguments; | ||
9 | for (int i = 0; i < argc; i++) { | ||
10 | arguments << argv[i]; | ||
11 | } | ||
12 | auto testDataPath = arguments.value(1); | ||
13 | auto dbName = arguments.value(2); | ||
14 | auto count = arguments.value(3).toInt(); | ||
15 | |||
16 | if (Sink::Storage::DataStore(testDataPath, dbName, Sink::Storage::DataStore::ReadOnly).exists()) { | ||
17 | Sink::Storage::DataStore(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite).removeFromDisk(); | ||
18 | } | ||
19 | |||
20 | qWarning() << "Creating db: " << testDataPath << dbName << count; | ||
21 | Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); | ||
22 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | ||
23 | for (int i = 0; i < count; i++) { | ||
24 | if (!transaction) { | ||
25 | qWarning() << "No valid transaction"; | ||
26 | return -1; | ||
27 | } | ||
28 | transaction.openDatabase("a", nullptr, false).write(QByteArray::number(i), "a"); | ||
29 | transaction.openDatabase("b", nullptr, false).write(QByteArray::number(i), "b"); | ||
30 | transaction.openDatabase("c", nullptr, false).write(QByteArray::number(i), "c"); | ||
31 | transaction.openDatabase("p", nullptr, false).write(QByteArray::number(i), "c"); | ||
32 | transaction.openDatabase("q", nullptr, false).write(QByteArray::number(i), "c"); | ||
33 | if (i > (count/2)) { | ||
34 | for (int d = 0; d < 40; d++) { | ||
35 | transaction.openDatabase("db" + QByteArray::number(d), nullptr, false).write(QByteArray::number(i), "a"); | ||
36 | } | ||
37 | } | ||
38 | if ((i % 1000) == 0) { | ||
39 | transaction.commit(); | ||
40 | transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | ||
41 | } | ||
42 | } | ||
43 | qWarning() << "Creating db done."; | ||
44 | return 0; | ||
45 | } | ||
diff --git a/tests/domainadaptortest.cpp b/tests/domainadaptortest.cpp index a17152e..2aed0a9 100644 --- a/tests/domainadaptortest.cpp +++ b/tests/domainadaptortest.cpp | |||
@@ -14,24 +14,22 @@ | |||
14 | #include "metadata_generated.h" | 14 | #include "metadata_generated.h" |
15 | #include "entity_generated.h" | 15 | #include "entity_generated.h" |
16 | 16 | ||
17 | class TestFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Event, Sink::ApplicationDomain::Buffer::Event, Sink::ApplicationDomain::Buffer::EventBuilder> | 17 | class TestFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Event> |
18 | { | 18 | { |
19 | public: | 19 | public: |
20 | TestFactory() | 20 | TestFactory() = default; |
21 | { | ||
22 | mResourceWriteMapper = QSharedPointer<WritePropertyMapper<Sink::ApplicationDomain::Buffer::EventBuilder>>::create(); | ||
23 | Sink::ApplicationDomain::TypeImplementation<Sink::ApplicationDomain::Event>::configure(*mResourceWriteMapper); | ||
24 | } | ||
25 | }; | 21 | }; |
26 | 22 | ||
27 | class TestMailFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Mail, Sink::ApplicationDomain::Buffer::Mail, Sink::ApplicationDomain::Buffer::MailBuilder> | 23 | class TestMailFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Mail> |
28 | { | 24 | { |
29 | public: | 25 | public: |
30 | TestMailFactory() | 26 | TestMailFactory() = default; |
31 | { | 27 | }; |
32 | mResourceWriteMapper = QSharedPointer<WritePropertyMapper<Sink::ApplicationDomain::Buffer::MailBuilder>>::create(); | 28 | |
33 | Sink::ApplicationDomain::TypeImplementation<Sink::ApplicationDomain::Mail>::configure(*mResourceWriteMapper); | 29 | class TestContactFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Contact> |
34 | } | 30 | { |
31 | public: | ||
32 | TestContactFactory() = default; | ||
35 | }; | 33 | }; |
36 | 34 | ||
37 | /** | 35 | /** |
@@ -51,7 +49,7 @@ private slots: | |||
51 | 49 | ||
52 | void testCreateBufferPart() | 50 | void testCreateBufferPart() |
53 | { | 51 | { |
54 | auto writeMapper = QSharedPointer<WritePropertyMapper<Sink::ApplicationDomain::Buffer::EventBuilder>>::create(); | 52 | auto writeMapper = QSharedPointer<PropertyMapper>::create(); |
55 | Sink::ApplicationDomain::TypeImplementation<Sink::ApplicationDomain::Event>::configure(*writeMapper); | 53 | Sink::ApplicationDomain::TypeImplementation<Sink::ApplicationDomain::Event>::configure(*writeMapper); |
56 | 54 | ||
57 | Sink::ApplicationDomain::Event event; | 55 | Sink::ApplicationDomain::Event event; |
@@ -104,7 +102,7 @@ private slots: | |||
104 | 102 | ||
105 | void testMail() | 103 | void testMail() |
106 | { | 104 | { |
107 | auto writeMapper = QSharedPointer<WritePropertyMapper<Sink::ApplicationDomain::Buffer::MailBuilder>>::create(); | 105 | auto writeMapper = QSharedPointer<PropertyMapper>::create(); |
108 | Sink::ApplicationDomain::TypeImplementation<Sink::ApplicationDomain::Mail>::configure(*writeMapper); | 106 | Sink::ApplicationDomain::TypeImplementation<Sink::ApplicationDomain::Mail>::configure(*writeMapper); |
109 | 107 | ||
110 | Sink::ApplicationDomain::Mail mail; | 108 | Sink::ApplicationDomain::Mail mail; |
@@ -139,6 +137,43 @@ private slots: | |||
139 | } | 137 | } |
140 | 138 | ||
141 | } | 139 | } |
140 | |||
141 | void testContact() | ||
142 | { | ||
143 | auto writeMapper = QSharedPointer<PropertyMapper>::create(); | ||
144 | Sink::ApplicationDomain::TypeImplementation<Sink::ApplicationDomain::Contact>::configure(*writeMapper); | ||
145 | |||
146 | auto binaryData = QByteArray::fromRawData("\xEF\xBF\xBD\x00\xEF\xBF", 5); | ||
147 | |||
148 | Sink::ApplicationDomain::Contact contact; | ||
149 | contact.setPhoto(binaryData); | ||
150 | QVERIFY(!contact.getPhoto().isEmpty()); | ||
151 | |||
152 | flatbuffers::FlatBufferBuilder metadataFbb; | ||
153 | auto metadataBuilder = Sink::MetadataBuilder(metadataFbb); | ||
154 | metadataBuilder.add_revision(1); | ||
155 | auto metadataBuffer = metadataBuilder.Finish(); | ||
156 | Sink::FinishMetadataBuffer(metadataFbb, metadataBuffer); | ||
157 | |||
158 | flatbuffers::FlatBufferBuilder mailFbb; | ||
159 | auto pos = createBufferPart<Sink::ApplicationDomain::Buffer::ContactBuilder, Sink::ApplicationDomain::Buffer::Contact>(contact, mailFbb, *writeMapper); | ||
160 | Sink::ApplicationDomain::Buffer::FinishContactBuffer(mailFbb, pos); | ||
161 | |||
162 | flatbuffers::FlatBufferBuilder fbb; | ||
163 | Sink::EntityBuffer::assembleEntityBuffer( | ||
164 | fbb, metadataFbb.GetBufferPointer(), metadataFbb.GetSize(), mailFbb.GetBufferPointer(), mailFbb.GetSize(), mailFbb.GetBufferPointer(), mailFbb.GetSize()); | ||
165 | |||
166 | { | ||
167 | std::string data(reinterpret_cast<const char *>(fbb.GetBufferPointer()), fbb.GetSize()); | ||
168 | Sink::EntityBuffer buffer((void *)(data.data()), data.size()); | ||
169 | |||
170 | TestContactFactory factory; | ||
171 | auto adaptor = factory.createAdaptor(buffer.entity()); | ||
172 | Sink::ApplicationDomain::Contact readContact{QByteArray{}, QByteArray{}, 0, adaptor}; | ||
173 | QCOMPARE(readContact.getPhoto(), contact.getPhoto()); | ||
174 | } | ||
175 | |||
176 | } | ||
142 | }; | 177 | }; |
143 | 178 | ||
144 | QTEST_MAIN(DomainAdaptorTest) | 179 | QTEST_MAIN(DomainAdaptorTest) |
diff --git a/tests/hawd/state.cpp b/tests/hawd/state.cpp index dfeef41..a994718 100644 --- a/tests/hawd/state.cpp +++ b/tests/hawd/state.cpp | |||
@@ -117,7 +117,7 @@ void State::findGitHash() | |||
117 | { | 117 | { |
118 | #ifdef HAVE_LIBGIT2 | 118 | #ifdef HAVE_LIBGIT2 |
119 | git_libgit2_init(); | 119 | git_libgit2_init(); |
120 | git_buf root = {0}; | 120 | git_buf root = GIT_BUF_INIT_CONST(0, 0); |
121 | int error = git_repository_discover(&root, projectPath().toStdString().data(), 0, NULL); | 121 | int error = git_repository_discover(&root, projectPath().toStdString().data(), 0, NULL); |
122 | if (!error) { | 122 | if (!error) { |
123 | git_repository *repo = NULL; | 123 | git_repository *repo = NULL; |
diff --git a/tests/interresourcemovetest.cpp b/tests/interresourcemovetest.cpp index 3ac6ad4..5438bc7 100644 --- a/tests/interresourcemovetest.cpp +++ b/tests/interresourcemovetest.cpp | |||
@@ -48,7 +48,7 @@ class InterResourceMoveTest : public QObject | |||
48 | m.subject(true)->fromUnicodeString(subject, "utf8"); | 48 | m.subject(true)->fromUnicodeString(subject, "utf8"); |
49 | m.messageID(true)->setIdentifier(uid); | 49 | m.messageID(true)->setIdentifier(uid); |
50 | m.assemble(); | 50 | m.assemble(); |
51 | return m.encodedContent(); | 51 | return m.encodedContent(true); |
52 | } | 52 | } |
53 | 53 | ||
54 | private slots: | 54 | private slots: |
diff --git a/tests/mailquerybenchmark.cpp b/tests/mailquerybenchmark.cpp index b15c8d6..00c156d 100644 --- a/tests/mailquerybenchmark.cpp +++ b/tests/mailquerybenchmark.cpp | |||
@@ -78,7 +78,7 @@ class MailQueryBenchmark : public QObject | |||
78 | entityStore.commitTransaction(); | 78 | entityStore.commitTransaction(); |
79 | } | 79 | } |
80 | 80 | ||
81 | void testLoad(const Sink::Query &query, int count, int expectedSize) | 81 | void testLoad(const QByteArray &name, const Sink::Query &query, int count, int expectedSize) |
82 | { | 82 | { |
83 | const auto startingRss = getCurrentRSS(); | 83 | const auto startingRss = getCurrentRSS(); |
84 | 84 | ||
@@ -124,7 +124,7 @@ class MailQueryBenchmark : public QObject | |||
124 | std::cout << "Rss without db [kb]: " << rssWithoutDb / 1024 << std::endl; | 124 | std::cout << "Rss without db [kb]: " << rssWithoutDb / 1024 << std::endl; |
125 | std::cout << "Percentage error: " << percentageRssError << std::endl; | 125 | std::cout << "Percentage error: " << percentageRssError << std::endl; |
126 | 126 | ||
127 | HAWD::Dataset dataset("mail_query", mHawdState); | 127 | HAWD::Dataset dataset(QString{"mail_query"} + name, mHawdState); |
128 | HAWD::Dataset::Row row = dataset.row(); | 128 | HAWD::Dataset::Row row = dataset.row(); |
129 | row.setValue("rows", list.size()); | 129 | row.setValue("rows", list.size()); |
130 | row.setValue("queryResultPerMs", (qreal)list.size() / elapsed); | 130 | row.setValue("queryResultPerMs", (qreal)list.size() / elapsed); |
@@ -159,7 +159,7 @@ private slots: | |||
159 | query.limit(1000); | 159 | query.limit(1000); |
160 | 160 | ||
161 | populateDatabase(50000); | 161 | populateDatabase(50000); |
162 | testLoad(query, 50000, query.limit()); | 162 | testLoad({}, query, 50000, query.limit()); |
163 | } | 163 | } |
164 | 164 | ||
165 | void test50kThreadleader() | 165 | void test50kThreadleader() |
@@ -176,7 +176,7 @@ private slots: | |||
176 | 176 | ||
177 | int count = 50000; | 177 | int count = 50000; |
178 | populateDatabase(count, mailsPerFolder); | 178 | populateDatabase(count, mailsPerFolder); |
179 | testLoad(query, count, query.limit()); | 179 | testLoad("_threadleader", query, count, query.limit()); |
180 | } | 180 | } |
181 | }; | 181 | }; |
182 | 182 | ||
diff --git a/tests/mailsynctest.cpp b/tests/mailsynctest.cpp index c8ba9f1..75454c0 100644 --- a/tests/mailsynctest.cpp +++ b/tests/mailsynctest.cpp | |||
@@ -33,8 +33,6 @@ | |||
33 | using namespace Sink; | 33 | using namespace Sink; |
34 | using namespace Sink::ApplicationDomain; | 34 | using namespace Sink::ApplicationDomain; |
35 | 35 | ||
36 | SINK_DEBUG_AREA("mailsynctest") | ||
37 | |||
38 | void MailSyncTest::initTestCase() | 36 | void MailSyncTest::initTestCase() |
39 | { | 37 | { |
40 | Test::initTest(); | 38 | Test::initTest(); |
@@ -226,7 +224,8 @@ void MailSyncTest::testListNewSubFolder() | |||
226 | for (const auto &folder : folders) { | 224 | for (const auto &folder : folders) { |
227 | names << folder->getName(); | 225 | names << folder->getName(); |
228 | } | 226 | } |
229 | QVERIFY(names.contains("sub1")); | 227 | ASYNCVERIFY(names.contains("sub1")); |
228 | return KAsync::null(); | ||
230 | }); | 229 | }); |
231 | VERIFYEXEC(job); | 230 | VERIFYEXEC(job); |
232 | } | 231 | } |
@@ -254,7 +253,8 @@ void MailSyncTest::testListRemovedSubFolder() | |||
254 | for (const auto &folder : folders) { | 253 | for (const auto &folder : folders) { |
255 | names << folder->getName(); | 254 | names << folder->getName(); |
256 | } | 255 | } |
257 | QVERIFY(!names.contains("sub1")); | 256 | ASYNCVERIFY(!names.contains("sub1")); |
257 | return KAsync::null(); | ||
258 | }); | 258 | }); |
259 | VERIFYEXEC(job); | 259 | VERIFYEXEC(job); |
260 | } | 260 | } |
@@ -270,18 +270,19 @@ void MailSyncTest::testListMails() | |||
270 | VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); | 270 | VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); |
271 | 271 | ||
272 | auto job = Store::fetchAll<Mail>(query).then([](const QList<Mail::Ptr> &mails) { | 272 | auto job = Store::fetchAll<Mail>(query).then([](const QList<Mail::Ptr> &mails) { |
273 | QCOMPARE(mails.size(), 1); | 273 | ASYNCCOMPARE(mails.size(), 1); |
274 | auto mail = mails.first(); | 274 | auto mail = mails.first(); |
275 | QVERIFY(mail->getSubject().startsWith(QString("[Nepomuk] Jenkins build is still unstable"))); | 275 | ASYNCVERIFY(mail->getSubject().startsWith(QString("[Nepomuk] Jenkins build is still unstable"))); |
276 | const auto data = mail->getMimeMessage(); | 276 | const auto data = mail->getMimeMessage(); |
277 | QVERIFY(!data.isEmpty()); | 277 | ASYNCVERIFY(!data.isEmpty()); |
278 | 278 | ||
279 | KMime::Message m; | 279 | KMime::Message m; |
280 | m.setContent(data); | 280 | m.setContent(KMime::CRLFtoLF(data)); |
281 | m.parse(); | 281 | m.parse(); |
282 | QCOMPARE(mail->getSubject(), m.subject(true)->asUnicodeString()); | 282 | ASYNCCOMPARE(mail->getSubject(), m.subject(true)->asUnicodeString()); |
283 | QVERIFY(!mail->getFolder().isEmpty()); | 283 | ASYNCVERIFY(!mail->getFolder().isEmpty()); |
284 | QVERIFY(mail->getDate().isValid()); | 284 | ASYNCVERIFY(mail->getDate().isValid()); |
285 | return KAsync::null(); | ||
285 | }); | 286 | }); |
286 | VERIFYEXEC(job); | 287 | VERIFYEXEC(job); |
287 | } | 288 | } |
@@ -302,10 +303,11 @@ void MailSyncTest::testResyncMails() | |||
302 | VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); | 303 | VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); |
303 | 304 | ||
304 | auto job = Store::fetchAll<Mail>(query).then([](const QList<Mail::Ptr> &mails) { | 305 | auto job = Store::fetchAll<Mail>(query).then([](const QList<Mail::Ptr> &mails) { |
305 | QCOMPARE(mails.size(), 1); | 306 | ASYNCCOMPARE(mails.size(), 1); |
306 | auto mail = mails.first(); | 307 | auto mail = mails.first(); |
307 | QVERIFY(!mail->getSubject().isEmpty()); | 308 | ASYNCVERIFY(!mail->getSubject().isEmpty()); |
308 | QVERIFY(!mail->getMimeMessagePath().isEmpty()); | 309 | ASYNCVERIFY(!mail->getMimeMessagePath().isEmpty()); |
310 | return KAsync::null(); | ||
309 | }); | 311 | }); |
310 | VERIFYEXEC(job); | 312 | VERIFYEXEC(job); |
311 | } | 313 | } |
@@ -330,7 +332,8 @@ void MailSyncTest::testFetchNewRemovedMessages() | |||
330 | 332 | ||
331 | { | 333 | { |
332 | auto job = Store::fetchAll<Mail>(query).then([](const QList<Mail::Ptr> &mails) { | 334 | auto job = Store::fetchAll<Mail>(query).then([](const QList<Mail::Ptr> &mails) { |
333 | QCOMPARE(mails.size(), 2); | 335 | ASYNCCOMPARE(mails.size(), 2); |
336 | return KAsync::null(); | ||
334 | }); | 337 | }); |
335 | VERIFYEXEC(job); | 338 | VERIFYEXEC(job); |
336 | } | 339 | } |
@@ -342,7 +345,8 @@ void MailSyncTest::testFetchNewRemovedMessages() | |||
342 | 345 | ||
343 | { | 346 | { |
344 | auto job = Store::fetchAll<Mail>(query).then([](const QList<Mail::Ptr> &mails) { | 347 | auto job = Store::fetchAll<Mail>(query).then([](const QList<Mail::Ptr> &mails) { |
345 | QCOMPARE(mails.size(), 1); | 348 | ASYNCCOMPARE(mails.size(), 1); |
349 | return KAsync::null(); | ||
346 | }); | 350 | }); |
347 | VERIFYEXEC(job); | 351 | VERIFYEXEC(job); |
348 | } | 352 | } |
@@ -365,7 +369,8 @@ void MailSyncTest::testFlagChange() | |||
365 | 369 | ||
366 | { | 370 | { |
367 | auto job = Store::fetchAll<Mail>(query).then([](const QList<Mail::Ptr> &mails) { | 371 | auto job = Store::fetchAll<Mail>(query).then([](const QList<Mail::Ptr> &mails) { |
368 | QCOMPARE(mails.size(), 0); | 372 | ASYNCCOMPARE(mails.size(), 0); |
373 | return KAsync::null(); | ||
369 | }); | 374 | }); |
370 | VERIFYEXEC(job); | 375 | VERIFYEXEC(job); |
371 | } | 376 | } |
@@ -378,8 +383,9 @@ void MailSyncTest::testFlagChange() | |||
378 | 383 | ||
379 | { | 384 | { |
380 | auto job = Store::fetchAll<Mail>(query).then([](const QList<Mail::Ptr> &mails) { | 385 | auto job = Store::fetchAll<Mail>(query).then([](const QList<Mail::Ptr> &mails) { |
381 | QCOMPARE(mails.size(), 1); | 386 | ASYNCCOMPARE(mails.size(), 1); |
382 | QVERIFY(mails.first()->getImportant()); | 387 | ASYNCVERIFY(mails.first()->getImportant()); |
388 | return KAsync::null(); | ||
383 | }); | 389 | }); |
384 | VERIFYEXEC(job); | 390 | VERIFYEXEC(job); |
385 | } | 391 | } |
@@ -394,8 +400,9 @@ void MailSyncTest::testSyncSingleFolder() | |||
394 | Folder::Ptr folder; | 400 | Folder::Ptr folder; |
395 | { | 401 | { |
396 | auto job = Store::fetchAll<Folder>(Sink::Query{}.resourceFilter(mResourceInstanceIdentifier).filter<Folder::Name>("test")).template then([&](const QList<Folder::Ptr> &folders) { | 402 | auto job = Store::fetchAll<Folder>(Sink::Query{}.resourceFilter(mResourceInstanceIdentifier).filter<Folder::Name>("test")).template then([&](const QList<Folder::Ptr> &folders) { |
397 | QCOMPARE(folders.size(), 1); | 403 | ASYNCCOMPARE(folders.size(), 1); |
398 | folder = folders.first(); | 404 | folder = folders.first(); |
405 | return KAsync::null(); | ||
399 | }); | 406 | }); |
400 | VERIFYEXEC(job); | 407 | VERIFYEXEC(job); |
401 | } | 408 | } |
@@ -419,11 +426,13 @@ void MailSyncTest::testSyncSingleMail() | |||
419 | Mail::Ptr mail; | 426 | Mail::Ptr mail; |
420 | { | 427 | { |
421 | auto job = Store::fetchAll<Mail>(Sink::Query{}.resourceFilter(mResourceInstanceIdentifier)).template then([&](const QList<Mail::Ptr> &mails) { | 428 | auto job = Store::fetchAll<Mail>(Sink::Query{}.resourceFilter(mResourceInstanceIdentifier)).template then([&](const QList<Mail::Ptr> &mails) { |
422 | QVERIFY(mails.size() >= 1); | 429 | ASYNCVERIFY(mails.size() >= 1); |
423 | mail = mails.first(); | 430 | mail = mails.first(); |
431 | return KAsync::null(); | ||
424 | }); | 432 | }); |
425 | VERIFYEXEC(job); | 433 | VERIFYEXEC(job); |
426 | } | 434 | } |
435 | QVERIFY(mail); | ||
427 | 436 | ||
428 | auto syncScope = Sink::SyncScope{ApplicationDomain::getTypeName<Mail>()}; | 437 | auto syncScope = Sink::SyncScope{ApplicationDomain::getTypeName<Mail>()}; |
429 | syncScope.resourceFilter(mResourceInstanceIdentifier); | 438 | syncScope.resourceFilter(mResourceInstanceIdentifier); |
diff --git a/tests/mailtest.cpp b/tests/mailtest.cpp index c51fc56..cbb56d5 100644 --- a/tests/mailtest.cpp +++ b/tests/mailtest.cpp | |||
@@ -31,8 +31,6 @@ | |||
31 | using namespace Sink; | 31 | using namespace Sink; |
32 | using namespace Sink::ApplicationDomain; | 32 | using namespace Sink::ApplicationDomain; |
33 | 33 | ||
34 | SINK_DEBUG_AREA("mailtest") | ||
35 | |||
36 | void MailTest::initTestCase() | 34 | void MailTest::initTestCase() |
37 | { | 35 | { |
38 | Test::initTest(); | 36 | Test::initTest(); |
@@ -154,7 +152,7 @@ void MailTest::testCreateModifyDeleteMail() | |||
154 | message->assemble(); | 152 | message->assemble(); |
155 | 153 | ||
156 | auto mail = Mail::create(mResourceInstanceIdentifier); | 154 | auto mail = Mail::create(mResourceInstanceIdentifier); |
157 | mail.setMimeMessage(message->encodedContent()); | 155 | mail.setMimeMessage(message->encodedContent(true)); |
158 | mail.setFolder(folder); | 156 | mail.setFolder(folder); |
159 | 157 | ||
160 | VERIFYEXEC(Store::create(mail)); | 158 | VERIFYEXEC(Store::create(mail)); |
@@ -168,7 +166,7 @@ void MailTest::testCreateModifyDeleteMail() | |||
168 | QCOMPARE(mail.getFolder(), folder.identifier()); | 166 | QCOMPARE(mail.getFolder(), folder.identifier()); |
169 | QVERIFY(QFile(mail.getMimeMessagePath()).exists()); | 167 | QVERIFY(QFile(mail.getMimeMessagePath()).exists()); |
170 | KMime::Message m; | 168 | KMime::Message m; |
171 | m.setContent(mail.getMimeMessage()); | 169 | m.setContent(KMime::CRLFtoLF(mail.getMimeMessage())); |
172 | m.parse(); | 170 | m.parse(); |
173 | QCOMPARE(m.subject(true)->asUnicodeString(), subject); | 171 | QCOMPARE(m.subject(true)->asUnicodeString(), subject); |
174 | }); | 172 | }); |
@@ -184,7 +182,7 @@ void MailTest::testCreateModifyDeleteMail() | |||
184 | auto message2 = KMime::Message::Ptr::create(); | 182 | auto message2 = KMime::Message::Ptr::create(); |
185 | message2->subject(true)->fromUnicodeString(subject2, "utf8"); | 183 | message2->subject(true)->fromUnicodeString(subject2, "utf8"); |
186 | message2->assemble(); | 184 | message2->assemble(); |
187 | mail.setMimeMessage(message2->encodedContent()); | 185 | mail.setMimeMessage(message2->encodedContent(true)); |
188 | 186 | ||
189 | VERIFYEXEC(Store::modify(mail)); | 187 | VERIFYEXEC(Store::modify(mail)); |
190 | VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); | 188 | VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); |
@@ -197,7 +195,7 @@ void MailTest::testCreateModifyDeleteMail() | |||
197 | QCOMPARE(mail.getFolder(), folder.identifier()); | 195 | QCOMPARE(mail.getFolder(), folder.identifier()); |
198 | QVERIFY(QFile(mail.getMimeMessagePath()).exists()); | 196 | QVERIFY(QFile(mail.getMimeMessagePath()).exists()); |
199 | KMime::Message m; | 197 | KMime::Message m; |
200 | m.setContent(mail.getMimeMessage()); | 198 | m.setContent(KMime::CRLFtoLF(mail.getMimeMessage())); |
201 | m.parse(); | 199 | m.parse(); |
202 | QCOMPARE(m.subject(true)->asUnicodeString(), subject2); | 200 | QCOMPARE(m.subject(true)->asUnicodeString(), subject2); |
203 | }); | 201 | }); |
@@ -239,7 +237,7 @@ void MailTest::testMoveMail() | |||
239 | message->assemble(); | 237 | message->assemble(); |
240 | 238 | ||
241 | auto mail = Mail::create(mResourceInstanceIdentifier); | 239 | auto mail = Mail::create(mResourceInstanceIdentifier); |
242 | mail.setMimeMessage(message->encodedContent()); | 240 | mail.setMimeMessage(message->encodedContent(true)); |
243 | mail.setFolder(folder); | 241 | mail.setFolder(folder); |
244 | 242 | ||
245 | VERIFYEXEC(Store::create(mail)); | 243 | VERIFYEXEC(Store::create(mail)); |
@@ -291,7 +289,7 @@ void MailTest::testMarkMailAsRead() | |||
291 | message->assemble(); | 289 | message->assemble(); |
292 | 290 | ||
293 | auto mail = Mail::create(mResourceInstanceIdentifier); | 291 | auto mail = Mail::create(mResourceInstanceIdentifier); |
294 | mail.setMimeMessage(message->encodedContent()); | 292 | mail.setMimeMessage(message->encodedContent(true)); |
295 | mail.setFolder(folder); | 293 | mail.setFolder(folder); |
296 | mail.setUnread(true); | 294 | mail.setUnread(true); |
297 | VERIFYEXEC(Store::create(mail)); | 295 | VERIFYEXEC(Store::create(mail)); |
@@ -343,7 +341,7 @@ void MailTest::testCreateDraft() | |||
343 | message->assemble(); | 341 | message->assemble(); |
344 | 342 | ||
345 | auto mail = ApplicationDomain::Mail::create(mResourceInstanceIdentifier); | 343 | auto mail = ApplicationDomain::Mail::create(mResourceInstanceIdentifier); |
346 | mail.setMimeMessage(message->encodedContent()); | 344 | mail.setMimeMessage(message->encodedContent(true)); |
347 | mail.setDraft(true); | 345 | mail.setDraft(true); |
348 | 346 | ||
349 | VERIFYEXEC(Store::create(mail)); | 347 | VERIFYEXEC(Store::create(mail)); |
@@ -393,7 +391,7 @@ void MailTest::testModifyMailToDraft() | |||
393 | message->assemble(); | 391 | message->assemble(); |
394 | 392 | ||
395 | auto mail = ApplicationDomain::Mail::create(mResourceInstanceIdentifier); | 393 | auto mail = ApplicationDomain::Mail::create(mResourceInstanceIdentifier); |
396 | mail.setMimeMessage(message->encodedContent()); | 394 | mail.setMimeMessage(message->encodedContent(true)); |
397 | mail.setDraft(false); | 395 | mail.setDraft(false); |
398 | mail.setFolder(folder); | 396 | mail.setFolder(folder); |
399 | 397 | ||
@@ -440,7 +438,7 @@ void MailTest::testModifyMailToTrash() | |||
440 | message->assemble(); | 438 | message->assemble(); |
441 | 439 | ||
442 | auto mail = ApplicationDomain::Mail::create(mResourceInstanceIdentifier); | 440 | auto mail = ApplicationDomain::Mail::create(mResourceInstanceIdentifier); |
443 | mail.setMimeMessage(message->encodedContent()); | 441 | mail.setMimeMessage(message->encodedContent(true)); |
444 | mail.setTrash(false); | 442 | mail.setTrash(false); |
445 | mail.setFolder(folder); | 443 | mail.setFolder(folder); |
446 | 444 | ||
diff --git a/tests/mailthreadtest.cpp b/tests/mailthreadtest.cpp index 02b29f6..741eb78 100644 --- a/tests/mailthreadtest.cpp +++ b/tests/mailthreadtest.cpp | |||
@@ -117,7 +117,7 @@ void MailThreadTest::testIndexInMixedOrder() | |||
117 | 117 | ||
118 | { | 118 | { |
119 | auto mail = Mail::create(mResourceInstanceIdentifier); | 119 | auto mail = Mail::create(mResourceInstanceIdentifier); |
120 | mail.setMimeMessage(message3->encodedContent()); | 120 | mail.setMimeMessage(message3->encodedContent(true)); |
121 | mail.setFolder(folder); | 121 | mail.setFolder(folder); |
122 | VERIFYEXEC(Store::create(mail)); | 122 | VERIFYEXEC(Store::create(mail)); |
123 | } | 123 | } |
@@ -140,7 +140,7 @@ void MailThreadTest::testIndexInMixedOrder() | |||
140 | 140 | ||
141 | { | 141 | { |
142 | auto mail = Mail::create(mResourceInstanceIdentifier); | 142 | auto mail = Mail::create(mResourceInstanceIdentifier); |
143 | mail.setMimeMessage(message2->encodedContent()); | 143 | mail.setMimeMessage(message2->encodedContent(true)); |
144 | mail.setFolder(folder); | 144 | mail.setFolder(folder); |
145 | VERIFYEXEC(Store::create(mail)); | 145 | VERIFYEXEC(Store::create(mail)); |
146 | } | 146 | } |
@@ -156,7 +156,7 @@ void MailThreadTest::testIndexInMixedOrder() | |||
156 | 156 | ||
157 | { | 157 | { |
158 | auto mail = Mail::create(mResourceInstanceIdentifier); | 158 | auto mail = Mail::create(mResourceInstanceIdentifier); |
159 | mail.setMimeMessage(message1->encodedContent()); | 159 | mail.setMimeMessage(message1->encodedContent(true)); |
160 | mail.setFolder(folder); | 160 | mail.setFolder(folder); |
161 | VERIFYEXEC(Store::create(mail)); | 161 | VERIFYEXEC(Store::create(mail)); |
162 | } | 162 | } |
diff --git a/tests/messagequeuetest.cpp b/tests/messagequeuetest.cpp index 83fa23f..2e3bd75 100644 --- a/tests/messagequeuetest.cpp +++ b/tests/messagequeuetest.cpp | |||
@@ -9,8 +9,6 @@ | |||
9 | #include "log.h" | 9 | #include "log.h" |
10 | #include "test.h" | 10 | #include "test.h" |
11 | 11 | ||
12 | SINK_DEBUG_AREA("messagequeuetest") | ||
13 | |||
14 | /** | 12 | /** |
15 | * Test of the messagequeue implementation. | 13 | * Test of the messagequeue implementation. |
16 | */ | 14 | */ |
diff --git a/tests/notificationtest.cpp b/tests/notificationtest.cpp index a34d325..f0d957e 100644 --- a/tests/notificationtest.cpp +++ b/tests/notificationtest.cpp | |||
@@ -67,12 +67,21 @@ private slots: | |||
67 | VERIFYEXEC(Sink::Store::synchronize(query)); | 67 | VERIFYEXEC(Sink::Store::synchronize(query)); |
68 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << "sink.dummy.instance1")); | 68 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << "sink.dummy.instance1")); |
69 | 69 | ||
70 | QVERIFY(statusNotifications.size() <= 3); | 70 | using namespace Sink::ApplicationDomain; |
71 | QTRY_COMPARE(statusNotifications.size(), 3); | 71 | { |
72 | //Sync | 72 | QList<Status> expected = { |
73 | QCOMPARE(statusNotifications.at(0).code, static_cast<int>(ApplicationDomain::Status::ConnectedStatus)); | 73 | Status::ConnectedStatus, |
74 | QCOMPARE(statusNotifications.at(1).code, static_cast<int>(ApplicationDomain::Status::BusyStatus)); | 74 | Status::BusyStatus, |
75 | QCOMPARE(statusNotifications.at(2).code, static_cast<int>(ApplicationDomain::Status::ConnectedStatus)); | 75 | Status::ConnectedStatus, |
76 | }; | ||
77 | qInfo() << "Received notifications " << statusNotifications; | ||
78 | QVERIFY2(statusNotifications.size() <= expected.size(), "More notifications than expected."); | ||
79 | QTRY_COMPARE(statusNotifications.size(), expected.size()); | ||
80 | qInfo() << "All received notifications " << statusNotifications; | ||
81 | for (auto i = 0; i < statusNotifications.size(); i++) { | ||
82 | QCOMPARE(statusNotifications.at(i).code, static_cast<int>(expected.at(i))); | ||
83 | } | ||
84 | } | ||
76 | //Changereplay | 85 | //Changereplay |
77 | // It can happen that we get a changereplay notification pair first and then a second one at the end, | 86 | // It can happen that we get a changereplay notification pair first and then a second one at the end, |
78 | // we therefore currently filter all changereplay notifications (see above). | 87 | // we therefore currently filter all changereplay notifications (see above). |
diff --git a/tests/querytest.cpp b/tests/querytest.cpp index f639d94..714e549 100644 --- a/tests/querytest.cpp +++ b/tests/querytest.cpp | |||
@@ -98,10 +98,7 @@ private slots: | |||
98 | void testSingle() | 98 | void testSingle() |
99 | { | 99 | { |
100 | // Setup | 100 | // Setup |
101 | { | 101 | VERIFYEXEC(Sink::Store::create<Mail>(Mail("sink.dummy.instance1"))); |
102 | Mail mail("sink.dummy.instance1"); | ||
103 | VERIFYEXEC(Sink::Store::create<Mail>(mail)); | ||
104 | } | ||
105 | 102 | ||
106 | // Test | 103 | // Test |
107 | Sink::Query query; | 104 | Sink::Query query; |
@@ -116,10 +113,7 @@ private slots: | |||
116 | void testSingleWithDelay() | 113 | void testSingleWithDelay() |
117 | { | 114 | { |
118 | // Setup | 115 | // Setup |
119 | { | 116 | VERIFYEXEC(Sink::Store::create<Mail>(Mail("sink.dummy.instance1"))); |
120 | Mail mail("sink.dummy.instance1"); | ||
121 | Sink::Store::create<Mail>(mail).exec().waitForFinished(); | ||
122 | } | ||
123 | 117 | ||
124 | // Test | 118 | // Test |
125 | Sink::Query query; | 119 | Sink::Query query; |
@@ -142,13 +136,13 @@ private slots: | |||
142 | Mail mail("sink.dummy.instance1"); | 136 | Mail mail("sink.dummy.instance1"); |
143 | mail.setExtractedMessageId("test1"); | 137 | mail.setExtractedMessageId("test1"); |
144 | mail.setFolder("folder1"); | 138 | mail.setFolder("folder1"); |
145 | Sink::Store::create<Mail>(mail).exec().waitForFinished(); | 139 | VERIFYEXEC(Sink::Store::create<Mail>(mail)); |
146 | } | 140 | } |
147 | { | 141 | { |
148 | Mail mail("sink.dummy.instance1"); | 142 | Mail mail("sink.dummy.instance1"); |
149 | mail.setExtractedMessageId("test2"); | 143 | mail.setExtractedMessageId("test2"); |
150 | mail.setFolder("folder2"); | 144 | mail.setFolder("folder2"); |
151 | Sink::Store::create<Mail>(mail).exec().waitForFinished(); | 145 | VERIFYEXEC(Sink::Store::create<Mail>(mail)); |
152 | } | 146 | } |
153 | 147 | ||
154 | // Test | 148 | // Test |
@@ -164,13 +158,13 @@ private slots: | |||
164 | auto mail = model->index(0, 0, QModelIndex()).data(Sink::Store::DomainObjectRole).value<Mail::Ptr>(); | 158 | auto mail = model->index(0, 0, QModelIndex()).data(Sink::Store::DomainObjectRole).value<Mail::Ptr>(); |
165 | { | 159 | { |
166 | mail->setFolder("folder2"); | 160 | mail->setFolder("folder2"); |
167 | Sink::Store::modify<Mail>(*mail).exec().waitForFinished(); | 161 | VERIFYEXEC(Sink::Store::modify<Mail>(*mail)); |
168 | } | 162 | } |
169 | QTRY_COMPARE(model->rowCount(), 0); | 163 | QTRY_COMPARE(model->rowCount(), 0); |
170 | 164 | ||
171 | { | 165 | { |
172 | mail->setFolder("folder1"); | 166 | mail->setFolder("folder1"); |
173 | Sink::Store::modify<Mail>(*mail).exec().waitForFinished(); | 167 | VERIFYEXEC(Sink::Store::modify<Mail>(*mail)); |
174 | } | 168 | } |
175 | QTRY_COMPARE(model->rowCount(), 1); | 169 | QTRY_COMPARE(model->rowCount(), 1); |
176 | } | 170 | } |
@@ -181,8 +175,8 @@ private slots: | |||
181 | // Setup | 175 | // Setup |
182 | { | 176 | { |
183 | Mail mail("sink.dummy.instance1"); | 177 | Mail mail("sink.dummy.instance1"); |
184 | Sink::Store::create<Mail>(mail).exec().waitForFinished(); | 178 | VERIFYEXEC(Sink::Store::create<Mail>(mail)); |
185 | Sink::Store::create<Mail>(mail).exec().waitForFinished(); | 179 | VERIFYEXEC(Sink::Store::create<Mail>(mail)); |
186 | 180 | ||
187 | Sink::Query query; | 181 | Sink::Query query; |
188 | query.resourceFilter("sink.dummy.instance1"); | 182 | query.resourceFilter("sink.dummy.instance1"); |
@@ -211,7 +205,7 @@ private slots: | |||
211 | // Setup | 205 | // Setup |
212 | { | 206 | { |
213 | Folder folder("sink.dummy.instance1"); | 207 | Folder folder("sink.dummy.instance1"); |
214 | Sink::Store::create<Folder>(folder).exec().waitForFinished(); | 208 | VERIFYEXEC(Sink::Store::create<Folder>(folder)); |
215 | } | 209 | } |
216 | 210 | ||
217 | // Test | 211 | // Test |
@@ -387,6 +381,7 @@ private slots: | |||
387 | { | 381 | { |
388 | // Setup | 382 | // Setup |
389 | Folder::Ptr folderEntity; | 383 | Folder::Ptr folderEntity; |
384 | const auto date = QDateTime(QDate(2015, 7, 7), QTime(12, 0)); | ||
390 | { | 385 | { |
391 | Folder folder("sink.dummy.instance1"); | 386 | Folder folder("sink.dummy.instance1"); |
392 | Sink::Store::create<Folder>(folder).exec().waitForFinished(); | 387 | Sink::Store::create<Folder>(folder).exec().waitForFinished(); |
@@ -404,7 +399,6 @@ private slots: | |||
404 | folderEntity = model->index(0, 0).data(Sink::Store::DomainObjectRole).value<Folder::Ptr>(); | 399 | folderEntity = model->index(0, 0).data(Sink::Store::DomainObjectRole).value<Folder::Ptr>(); |
405 | QVERIFY(!folderEntity->identifier().isEmpty()); | 400 | QVERIFY(!folderEntity->identifier().isEmpty()); |
406 | 401 | ||
407 | const auto date = QDateTime(QDate(2015, 7, 7), QTime(12, 0)); | ||
408 | { | 402 | { |
409 | Mail mail("sink.dummy.instance1"); | 403 | Mail mail("sink.dummy.instance1"); |
410 | mail.setExtractedMessageId("testSecond"); | 404 | mail.setExtractedMessageId("testSecond"); |
@@ -434,6 +428,11 @@ private slots: | |||
434 | query.filter<Mail::Folder>(*folderEntity); | 428 | query.filter<Mail::Folder>(*folderEntity); |
435 | query.sort<Mail::Date>(); | 429 | query.sort<Mail::Date>(); |
436 | query.limit(1); | 430 | query.limit(1); |
431 | query.setFlags(Query::LiveQuery); | ||
432 | query.reduce<ApplicationDomain::Mail::ThreadId>(Query::Reduce::Selector::max<ApplicationDomain::Mail::Date>()) | ||
433 | .count("count") | ||
434 | .collect<ApplicationDomain::Mail::Unread>("unreadCollected") | ||
435 | .collect<ApplicationDomain::Mail::Important>("importantCollected"); | ||
437 | 436 | ||
438 | // Ensure all local data is processed | 437 | // Ensure all local data is processed |
439 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << "sink.dummy.instance1")); | 438 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << "sink.dummy.instance1")); |
@@ -449,6 +448,26 @@ private slots: | |||
449 | QCOMPARE(model->rowCount(), 2); | 448 | QCOMPARE(model->rowCount(), 2); |
450 | // We can't make any assumptions about the order of the indexes | 449 | // We can't make any assumptions about the order of the indexes |
451 | // QCOMPARE(model->index(1, 0).data(Sink::Store::DomainObjectRole).value<Mail::Ptr>()->getProperty("messageId").toByteArray(), QByteArray("testSecond")); | 450 | // QCOMPARE(model->index(1, 0).data(Sink::Store::DomainObjectRole).value<Mail::Ptr>()->getProperty("messageId").toByteArray(), QByteArray("testSecond")); |
451 | |||
452 | //New revisions always go through | ||
453 | { | ||
454 | Mail mail("sink.dummy.instance1"); | ||
455 | mail.setExtractedMessageId("testInjected"); | ||
456 | mail.setFolder(folderEntity->identifier()); | ||
457 | mail.setExtractedDate(date.addDays(-2)); | ||
458 | Sink::Store::create<Mail>(mail).exec().waitForFinished(); | ||
459 | } | ||
460 | QTRY_COMPARE(model->rowCount(), 3); | ||
461 | |||
462 | //Ensure we can continue fetching after the incremental update | ||
463 | model->fetchMore(QModelIndex()); | ||
464 | QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); | ||
465 | QCOMPARE(model->rowCount(), 4); | ||
466 | |||
467 | //Ensure we have fetched all | ||
468 | model->fetchMore(QModelIndex()); | ||
469 | QTRY_VERIFY(model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()); | ||
470 | QCOMPARE(model->rowCount(), 4); | ||
452 | } | 471 | } |
453 | 472 | ||
454 | void testReactToNewResource() | 473 | void testReactToNewResource() |
@@ -503,12 +522,13 @@ private slots: | |||
503 | Folder folder2(resource2.identifier()); | 522 | Folder folder2(resource2.identifier()); |
504 | VERIFYEXEC(Sink::Store::create<Folder>(folder2)); | 523 | VERIFYEXEC(Sink::Store::create<Folder>(folder2)); |
505 | } | 524 | } |
525 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << resource1.identifier())); | ||
526 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(QByteArrayList() << resource2.identifier())); | ||
506 | 527 | ||
507 | // Test | 528 | // Test |
508 | Sink::Query query; | 529 | Sink::Query query; |
509 | query.resourceFilter<SinkResource::Account>(account1); | 530 | query.resourceFilter<SinkResource::Account>(account1); |
510 | 531 | ||
511 | // We fetch before the data is available and rely on the live query mechanism to deliver the actual data | ||
512 | auto folders = Sink::Store::read<Folder>(query); | 532 | auto folders = Sink::Store::read<Folder>(query); |
513 | QCOMPARE(folders.size(), 1); | 533 | QCOMPARE(folders.size(), 1); |
514 | } | 534 | } |
@@ -621,18 +641,50 @@ private slots: | |||
621 | resource2.setResourceType("sink.dummy"); | 641 | resource2.setResourceType("sink.dummy"); |
622 | VERIFYEXEC(Store::create(resource2)); | 642 | VERIFYEXEC(Store::create(resource2)); |
623 | 643 | ||
624 | Folder folder1(resource1.identifier()); | 644 | VERIFYEXEC(Sink::Store::create<Folder>(Folder{resource1.identifier()})); |
625 | VERIFYEXEC(Sink::Store::create<Folder>(folder1)); | 645 | VERIFYEXEC(Sink::Store::create<Folder>(Folder{resource2.identifier()})); |
626 | Folder folder2(resource2.identifier()); | ||
627 | VERIFYEXEC(Sink::Store::create<Folder>(folder2)); | ||
628 | 646 | ||
629 | // Test | 647 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(resource1.identifier())); |
630 | Sink::Query query; | 648 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(resource2.identifier())); |
631 | query.resourceContainsFilter<SinkResource::Capabilities>("cap1"); | ||
632 | 649 | ||
633 | // We fetch before the data is available and rely on the live query mechanism to deliver the actual data | 650 | // We fetch before the data is available and rely on the live query mechanism to deliver the actual data |
634 | auto folders = Sink::Store::read<Folder>(query); | 651 | auto folders = Sink::Store::read<Folder>(Sink::Query{}.resourceContainsFilter<SinkResource::Capabilities>("cap1")); |
635 | QCOMPARE(folders.size(), 1); | 652 | QCOMPARE(folders.size(), 1); |
653 | |||
654 | //TODO this should be part of the regular cleanup between tests | ||
655 | VERIFYEXEC(Store::remove(resource1)); | ||
656 | VERIFYEXEC(Store::remove(resource2)); | ||
657 | } | ||
658 | |||
659 | void testFilteredLiveResourceSubQuery() | ||
660 | { | ||
661 | using namespace Sink; | ||
662 | using namespace Sink::ApplicationDomain; | ||
663 | |||
664 | //Setup | ||
665 | auto resource1 = ApplicationDomainType::createEntity<SinkResource>(); | ||
666 | resource1.setResourceType("sink.dummy"); | ||
667 | resource1.setCapabilities(QByteArrayList() << "cap1"); | ||
668 | VERIFYEXEC(Store::create(resource1)); | ||
669 | VERIFYEXEC(Store::create<Folder>(Folder{resource1.identifier()})); | ||
670 | VERIFYEXEC(ResourceControl::flushMessageQueue(resource1.identifier())); | ||
671 | |||
672 | auto model = Sink::Store::loadModel<Folder>(Query{Query::LiveQuery}.resourceContainsFilter<SinkResource::Capabilities>("cap1")); | ||
673 | QTRY_COMPARE(model->rowCount(), 1); | ||
674 | |||
675 | auto resource2 = ApplicationDomainType::createEntity<SinkResource>(); | ||
676 | resource2.setCapabilities(QByteArrayList() << "cap2"); | ||
677 | resource2.setResourceType("sink.dummy"); | ||
678 | VERIFYEXEC(Store::create(resource2)); | ||
679 | VERIFYEXEC(Store::create<Folder>(Folder{resource2.identifier()})); | ||
680 | VERIFYEXEC(ResourceControl::flushMessageQueue(resource2.identifier())); | ||
681 | |||
682 | //The new resource should be filtered and thus not make it in here | ||
683 | QCOMPARE(model->rowCount(), 1); | ||
684 | |||
685 | //TODO this should be part of the regular cleanup between tests | ||
686 | VERIFYEXEC(Store::remove(resource1)); | ||
687 | VERIFYEXEC(Store::remove(resource2)); | ||
636 | } | 688 | } |
637 | 689 | ||
638 | void testLivequeryUnmatchInThread() | 690 | void testLivequeryUnmatchInThread() |
@@ -1059,30 +1111,18 @@ private slots: | |||
1059 | QDateTime now{QDate{2017, 2, 3}, QTime{10, 0, 0}}; | 1111 | QDateTime now{QDate{2017, 2, 3}, QTime{10, 0, 0}}; |
1060 | QDateTime later{QDate{2017, 2, 3}, QTime{11, 0, 0}}; | 1112 | QDateTime later{QDate{2017, 2, 3}, QTime{11, 0, 0}}; |
1061 | 1113 | ||
1062 | { | 1114 | auto createMail = [] (const QByteArray &messageid, const Folder &folder, const QDateTime &date, bool important) { |
1063 | auto mail1 = Mail::createEntity<Mail>("sink.dummy.instance1"); | 1115 | auto mail = Mail::createEntity<Mail>("sink.dummy.instance1"); |
1064 | mail1.setExtractedMessageId("mail1"); | 1116 | mail.setExtractedMessageId(messageid); |
1065 | mail1.setFolder(folder1); | 1117 | mail.setFolder(folder); |
1066 | mail1.setExtractedDate(now); | 1118 | mail.setExtractedDate(date); |
1067 | mail1.setImportant(false); | 1119 | mail.setImportant(important); |
1068 | VERIFYEXEC(Sink::Store::create(mail1)); | 1120 | return mail; |
1069 | } | 1121 | }; |
1070 | { | 1122 | |
1071 | auto mail2 = Mail::createEntity<Mail>("sink.dummy.instance1"); | 1123 | VERIFYEXEC(Sink::Store::create(createMail("mail1", folder1, now, false))); |
1072 | mail2.setExtractedMessageId("mail2"); | 1124 | VERIFYEXEC(Sink::Store::create(createMail("mail2", folder1, earlier, false))); |
1073 | mail2.setFolder(folder1); | 1125 | VERIFYEXEC(Sink::Store::create(createMail("mail3", folder1, later, true))); |
1074 | mail2.setExtractedDate(earlier); | ||
1075 | mail2.setImportant(false); | ||
1076 | VERIFYEXEC(Sink::Store::create(mail2)); | ||
1077 | } | ||
1078 | { | ||
1079 | auto mail3 = Mail::createEntity<Mail>("sink.dummy.instance1"); | ||
1080 | mail3.setExtractedMessageId("mail3"); | ||
1081 | mail3.setFolder(folder1); | ||
1082 | mail3.setExtractedDate(later); | ||
1083 | mail3.setImportant(true); | ||
1084 | VERIFYEXEC(Sink::Store::create(mail3)); | ||
1085 | } | ||
1086 | 1126 | ||
1087 | // Ensure all local data is processed | 1127 | // Ensure all local data is processed |
1088 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); | 1128 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1")); |
diff --git a/tests/storagetest.cpp b/tests/storagetest.cpp index 7202628..9e9bad9 100644 --- a/tests/storagetest.cpp +++ b/tests/storagetest.cpp | |||
@@ -487,6 +487,61 @@ private slots: | |||
487 | } | 487 | } |
488 | } | 488 | } |
489 | 489 | ||
490 | void testCopyTransaction() | ||
491 | { | ||
492 | Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); | ||
493 | { | ||
494 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | ||
495 | transaction.openDatabase("a", nullptr, false); | ||
496 | transaction.openDatabase("b", nullptr, false); | ||
497 | transaction.openDatabase("c", nullptr, false); | ||
498 | transaction.commit(); | ||
499 | } | ||
500 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadOnly); | ||
501 | for (int i = 0; i < 1000; i++) { | ||
502 | transaction.openDatabase("a", nullptr, false); | ||
503 | transaction.openDatabase("b", nullptr, false); | ||
504 | transaction.openDatabase("c", nullptr, false); | ||
505 | transaction = store.createTransaction(Sink::Storage::DataStore::ReadOnly); | ||
506 | } | ||
507 | } | ||
508 | |||
509 | /* | ||
510 | * This test is meant to find problems with the multi-process architecture and initial database creation. | ||
511 | * If we create named databases dynamically (not all up front), it is possilbe that we violate the rule | ||
512 | * that mdb_open_dbi may only be used by a single thread at a time. | ||
513 | * This test is meant to stress that condition. | ||
514 | * | ||
515 | * However, it yields absolutely nothing. | ||
516 | */ | ||
517 | void testReadDuringExternalProcessWrite() | ||
518 | { | ||
519 | QSKIP("Not running multiprocess test"); | ||
520 | |||
521 | QList<QFuture<void>> futures; | ||
522 | for (int i = 0; i < 5; i++) { | ||
523 | futures << QtConcurrent::run([&]() { | ||
524 | QTRY_VERIFY(Sink::Storage::DataStore(testDataPath, dbName, Sink::Storage::DataStore::ReadOnly).exists()); | ||
525 | Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadOnly); | ||
526 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadOnly); | ||
527 | for (int i = 0; i < 100000; i++) { | ||
528 | transaction.openDatabase("a", nullptr, false); | ||
529 | transaction.openDatabase("b", nullptr, false); | ||
530 | transaction.openDatabase("c", nullptr, false); | ||
531 | transaction.openDatabase("p", nullptr, false); | ||
532 | transaction.openDatabase("q", nullptr, false); | ||
533 | } | ||
534 | }); | ||
535 | } | ||
536 | |||
537 | //Start writing to the db from a separate process | ||
538 | QVERIFY(QProcess::startDetached(QCoreApplication::applicationDirPath() + "/dbwriter", QStringList() << testDataPath << dbName << QString::number(100000))); | ||
539 | |||
540 | for (auto future : futures) { | ||
541 | future.waitForFinished(); | ||
542 | } | ||
543 | |||
544 | } | ||
490 | }; | 545 | }; |
491 | 546 | ||
492 | QTEST_MAIN(StorageTest) | 547 | QTEST_MAIN(StorageTest) |
diff --git a/tests/testimplementations.h b/tests/testimplementations.h index a145265..ff9d9b8 100644 --- a/tests/testimplementations.h +++ b/tests/testimplementations.h | |||
@@ -33,7 +33,7 @@ | |||
33 | #include "mail_generated.h" | 33 | #include "mail_generated.h" |
34 | #include "createentity_generated.h" | 34 | #include "createentity_generated.h" |
35 | 35 | ||
36 | class TestEventAdaptorFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Event, Sink::ApplicationDomain::Buffer::Event, Sink::ApplicationDomain::Buffer::EventBuilder> | 36 | class TestEventAdaptorFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Event> |
37 | { | 37 | { |
38 | public: | 38 | public: |
39 | TestEventAdaptorFactory() : DomainTypeAdaptorFactory() | 39 | TestEventAdaptorFactory() : DomainTypeAdaptorFactory() |
@@ -43,7 +43,7 @@ public: | |||
43 | virtual ~TestEventAdaptorFactory(){}; | 43 | virtual ~TestEventAdaptorFactory(){}; |
44 | }; | 44 | }; |
45 | 45 | ||
46 | class TestMailAdaptorFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Mail, Sink::ApplicationDomain::Buffer::Mail, Sink::ApplicationDomain::Buffer::MailBuilder> | 46 | class TestMailAdaptorFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Mail> |
47 | { | 47 | { |
48 | public: | 48 | public: |
49 | TestMailAdaptorFactory() : DomainTypeAdaptorFactory() | 49 | TestMailAdaptorFactory() : DomainTypeAdaptorFactory() |