summaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2015-12-18 10:38:36 +0100
committerChristian Mollekopf <chrigi_1@fastmail.fm>2015-12-18 10:38:36 +0100
commit23e807c133a23f924d56bc860aa34f62f09109ff (patch)
tree92d8584902d1cc902c8a1b8d7464427035257ccf /examples
parent765f27cf52497bc401579db38f0011d90fb75cbb (diff)
downloadsink-23e807c133a23f924d56bc860aa34f62f09109ff.tar.gz
sink-23e807c133a23f924d56bc860aa34f62f09109ff.zip
Detect modifications and removals on folders in the maildirresource
Diffstat (limited to 'examples')
-rw-r--r--examples/maildirresource/maildirresource.cpp139
-rw-r--r--examples/maildirresource/maildirresource.h4
2 files changed, 112 insertions, 31 deletions
diff --git a/examples/maildirresource/maildirresource.cpp b/examples/maildirresource/maildirresource.cpp
index 54a73e9..10e9046 100644
--- a/examples/maildirresource/maildirresource.cpp
+++ b/examples/maildirresource/maildirresource.cpp
@@ -23,6 +23,8 @@
23#include "pipeline.h" 23#include "pipeline.h"
24#include "mail_generated.h" 24#include "mail_generated.h"
25#include "createentity_generated.h" 25#include "createentity_generated.h"
26#include "modifyentity_generated.h"
27#include "deleteentity_generated.h"
26#include "domainadaptor.h" 28#include "domainadaptor.h"
27#include "resourceconfig.h" 29#include "resourceconfig.h"
28#include "commands.h" 30#include "commands.h"
@@ -64,15 +66,27 @@ MaildirResource::MaildirResource(const QByteArray &instanceIdentifier, const QSh
64QString MaildirResource::resolveRemoteId(const QByteArray &bufferType, const QString &remoteId, Akonadi2::Storage::Transaction &transaction) 66QString MaildirResource::resolveRemoteId(const QByteArray &bufferType, const QString &remoteId, Akonadi2::Storage::Transaction &transaction)
65{ 67{
66 //Lookup local id for remote id, or insert a new pair otherwise 68 //Lookup local id for remote id, or insert a new pair otherwise
67 auto remoteIdWithType = bufferType + remoteId.toUtf8(); 69 Index index("rid.mapping." + bufferType, transaction);
68 QByteArray akonadiId = Index("rid.mapping", transaction).lookup(remoteIdWithType); 70 Index localIndex("localid.mapping." + bufferType, transaction);
71 QByteArray akonadiId = index.lookup(remoteId.toUtf8());
69 if (akonadiId.isEmpty()) { 72 if (akonadiId.isEmpty()) {
70 akonadiId = QUuid::createUuid().toString().toUtf8(); 73 akonadiId = QUuid::createUuid().toString().toUtf8();
71 Index("rid.mapping", transaction).add(remoteIdWithType, akonadiId); 74 index.add(remoteId.toUtf8(), akonadiId);
75 localIndex.add(akonadiId, remoteId.toUtf8());
72 } 76 }
73 return akonadiId; 77 return akonadiId;
74} 78}
75 79
80QString MaildirResource::resolveLocalId(const QByteArray &bufferType, const QByteArray &localId, Akonadi2::Storage::Transaction &transaction)
81{
82 Index index("localid.mapping." + bufferType, transaction);
83 QByteArray remoteId = index.lookup(localId);
84 if (remoteId.isEmpty()) {
85 Warning() << "Couldn't find the local id";
86 }
87 return remoteId;
88}
89
76static QStringList listRecursive( const QString &root, const KPIM::Maildir &dir ) 90static QStringList listRecursive( const QString &root, const KPIM::Maildir &dir )
77{ 91{
78 QStringList list; 92 QStringList list;
@@ -114,6 +128,50 @@ static void createEntity(const QByteArray &akonadiId, const QByteArray &bufferTy
114 callback(QByteArray::fromRawData(reinterpret_cast<char const *>(fbb.GetBufferPointer()), fbb.GetSize())); 128 callback(QByteArray::fromRawData(reinterpret_cast<char const *>(fbb.GetBufferPointer()), fbb.GetSize()));
115} 129}
116 130
131static void modifyEntity(const QByteArray &akonadiId, qint64 revision, const QByteArray &bufferType, Akonadi2::ApplicationDomain::ApplicationDomainType &domainObject, DomainTypeAdaptorFactoryInterface &adaptorFactory, std::function<void(const QByteArray &)> callback)
132{
133 flatbuffers::FlatBufferBuilder entityFbb;
134 adaptorFactory.createBuffer(domainObject, entityFbb);
135 flatbuffers::FlatBufferBuilder fbb;
136 auto entityId = fbb.CreateString(akonadiId.toStdString());
137 //This is the resource type and not the domain type
138 auto type = fbb.CreateString(bufferType.toStdString());
139 auto delta = Akonadi2::EntityBuffer::appendAsVector(fbb, entityFbb.GetBufferPointer(), entityFbb.GetSize());
140 //TODO removals
141 auto location = Akonadi2::Commands::CreateModifyEntity(fbb, revision, entityId, 0, type, delta);
142 Akonadi2::Commands::FinishModifyEntityBuffer(fbb, location);
143 callback(QByteArray::fromRawData(reinterpret_cast<char const *>(fbb.GetBufferPointer()), fbb.GetSize()));
144}
145
146static void deleteEntity(const QByteArray &akonadiId, qint64 revision, const QByteArray &bufferType, std::function<void(const QByteArray &)> callback)
147{
148 flatbuffers::FlatBufferBuilder fbb;
149 auto entityId = fbb.CreateString(akonadiId.toStdString());
150 //This is the resource type and not the domain type
151 auto type = fbb.CreateString(bufferType.toStdString());
152 auto location = Akonadi2::Commands::CreateDeleteEntity(fbb, revision, entityId, type);
153 Akonadi2::Commands::FinishDeleteEntityBuffer(fbb, location);
154 callback(QByteArray::fromRawData(reinterpret_cast<char const *>(fbb.GetBufferPointer()), fbb.GetSize()));
155}
156
157static QSharedPointer<Akonadi2::ApplicationDomain::BufferAdaptor> getLatest(const Akonadi2::Storage::NamedDatabase &db, const QByteArray &uid, DomainTypeAdaptorFactoryInterface &adaptorFactory)
158{
159 QSharedPointer<Akonadi2::ApplicationDomain::BufferAdaptor> current;
160 db.findLatest(uid, [&current, &adaptorFactory](const QByteArray &key, const QByteArray &data) -> bool {
161 Akonadi2::EntityBuffer buffer(const_cast<const char *>(data.data()), data.size());
162 if (!buffer.isValid()) {
163 Warning() << "Read invalid buffer from disk";
164 } else {
165 current = adaptorFactory.createAdaptor(buffer.entity());
166 }
167 return false;
168 },
169 [](const Akonadi2::Storage::Error &error) {
170 Warning() << "Failed to read current value from storage: " << error.message;
171 });
172 return current;
173}
174
117void MaildirResource::synchronizeFolders(Akonadi2::Storage::Transaction &transaction) 175void MaildirResource::synchronizeFolders(Akonadi2::Storage::Transaction &transaction)
118{ 176{
119 const QByteArray bufferType = ENTITY_TYPE_FOLDER; 177 const QByteArray bufferType = ENTITY_TYPE_FOLDER;
@@ -122,41 +180,66 @@ void MaildirResource::synchronizeFolders(Akonadi2::Storage::Transaction &transac
122 180
123 Akonadi2::Storage store(Akonadi2::storageLocation(), mResourceInstanceIdentifier + ".synchronization", Akonadi2::Storage::ReadWrite); 181 Akonadi2::Storage store(Akonadi2::storageLocation(), mResourceInstanceIdentifier + ".synchronization", Akonadi2::Storage::ReadWrite);
124 auto synchronizationTransaction = store.createTransaction(Akonadi2::Storage::ReadWrite); 182 auto synchronizationTransaction = store.createTransaction(Akonadi2::Storage::ReadWrite);
125 for (const auto folder : folderList) { 183 auto mainDatabase = transaction.openDatabase(bufferType + ".main");
126 const auto remoteId = folder.toUtf8(); 184
185 //TODO Instead of iterating over all entries in the database, which can also pick up the same item multiple times,
186 //we should rather iterate over an index that contains every uid exactly once. The remoteId index would be such an index,
187 //but we currently fail to iterate over all entries in an index it seems.
188 // auto remoteIds = synchronizationTransaction.openDatabase("rid.mapping." + bufferType, std::function<void(const Akonadi2::Storage::Error &)>(), true);
189 mainDatabase.scan("", [&folderList, this, &transaction, bufferType, &synchronizationTransaction](const QByteArray &key, const QByteArray &) {
190 auto akonadiId = Akonadi2::Storage::uidFromKey(key);
191 const auto remoteId = resolveLocalId(bufferType, akonadiId, synchronizationTransaction);
192 if (!folderList.contains(remoteId)) {
193 Trace() << "Found a removed entity: " << akonadiId;
194 deleteEntity(akonadiId, Akonadi2::Storage::maxRevision(transaction), bufferType, [this](const QByteArray &buffer) {
195 enqueueCommand(mSynchronizerQueue, Akonadi2::Commands::DeleteEntityCommand, buffer);
196 });
197 }
198 return true;
199 },
200 [](const Akonadi2::Storage::Error &error) {
201 });
202
203 for (const auto folderPath : folderList) {
204 const auto remoteId = folderPath.toUtf8();
127 Trace() << "Processing folder " << remoteId; 205 Trace() << "Processing folder " << remoteId;
128 auto akonadiId = resolveRemoteId(bufferType, remoteId, synchronizationTransaction); 206 const auto akonadiId = resolveRemoteId(bufferType, remoteId, synchronizationTransaction).toLatin1();
207 const auto found = mainDatabase.contains(akonadiId);
129 208
130 bool found = false; 209 KPIM::Maildir md(folderPath, folderPath == mMaildirPath);
131 transaction.openDatabase(bufferType + ".main").scan(akonadiId.toUtf8(), [&found](const QByteArray &, const QByteArray &) -> bool {
132 found = true;
133 return false;
134 }, [this](const Akonadi2::Storage::Error &error) {
135 }, true);
136 210
137 if (!found) { //A new entity 211 Akonadi2::ApplicationDomain::Folder folder;
138 KPIM::Maildir md(folder, folder == mMaildirPath); 212 folder.setProperty("name", md.name());
139 213 folder.setProperty("icon", "folder");
140 Akonadi2::ApplicationDomain::Folder folder; 214 if (!md.isRoot()) {
141 folder.setProperty("name", md.name()); 215 folder.setProperty("parent", resolveRemoteId(ENTITY_TYPE_FOLDER, md.parent().path(), synchronizationTransaction).toLatin1());
142 folder.setProperty("icon", "folder"); 216 }
143 if (!md.isRoot()) {
144 Trace() << "subfolder parent: " << md.parent().path();
145 auto akonadiId = resolveRemoteId(ENTITY_TYPE_FOLDER, md.parent().path(), synchronizationTransaction);
146 folder.setProperty("parent", akonadiId);
147 }
148 217
218 if (!found) {
149 Trace() << "Found a new entity: " << remoteId; 219 Trace() << "Found a new entity: " << remoteId;
150 createEntity(akonadiId.toLatin1(), bufferType, folder, *mFolderAdaptorFactory, [this](const QByteArray &buffer) { 220 createEntity(akonadiId, bufferType, folder, *mFolderAdaptorFactory, [this](const QByteArray &buffer) {
151 enqueueCommand(mSynchronizerQueue, Akonadi2::Commands::CreateEntityCommand, buffer); 221 enqueueCommand(mSynchronizerQueue, Akonadi2::Commands::CreateEntityCommand, buffer);
152 }); 222 });
153
154 } else { //modification 223 } else { //modification
155 Trace() << "Found a modified entity: " << remoteId; 224 if (auto current = getLatest(mainDatabase, akonadiId, *mFolderAdaptorFactory)) {
156 //TODO diff and create modification if necessary 225 bool changed = false;
226 for (const auto &property : folder.changedProperties()) {
227 if (folder.getProperty(property) != current->getProperty(property)) {
228 Trace() << "Property changed " << akonadiId << property;
229 changed = true;
230 }
231 }
232 if (changed) {
233 Trace() << "Found a modified entity: " << remoteId;
234 modifyEntity(akonadiId, Akonadi2::Storage::maxRevision(transaction), bufferType, folder, *mFolderAdaptorFactory, [this](const QByteArray &buffer) {
235 enqueueCommand(mSynchronizerQueue, Akonadi2::Commands::ModifyEntityCommand, buffer);
236 });
237 }
238 } else {
239 Warning() << "Failed to get current entity";
240 }
157 } 241 }
158 } 242 }
159 //TODO find items to remove
160} 243}
161 244
162void MaildirResource::synchronizeMails(Akonadi2::Storage::Transaction &transaction, const QString &path) 245void MaildirResource::synchronizeMails(Akonadi2::Storage::Transaction &transaction, const QString &path)
diff --git a/examples/maildirresource/maildirresource.h b/examples/maildirresource/maildirresource.h
index dbf849a..e1eecc1 100644
--- a/examples/maildirresource/maildirresource.h
+++ b/examples/maildirresource/maildirresource.h
@@ -40,9 +40,7 @@ public:
40private: 40private:
41 KAsync::Job<void> replay(const QByteArray &type, const QByteArray &key, const QByteArray &value) Q_DECL_OVERRIDE; 41 KAsync::Job<void> replay(const QByteArray &type, const QByteArray &key, const QByteArray &value) Q_DECL_OVERRIDE;
42 QString resolveRemoteId(const QByteArray &type, const QString &remoteId, Akonadi2::Storage::Transaction &transaction); 42 QString resolveRemoteId(const QByteArray &type, const QString &remoteId, Akonadi2::Storage::Transaction &transaction);
43 // void createMail(const QByteArray &rid, const QMap<QString, QVariant> &data, flatbuffers::FlatBufferBuilder &entityFbb, Akonadi2::Storage::Transaction &); 43 QString resolveLocalId(const QByteArray &bufferType, const QByteArray &localId, Akonadi2::Storage::Transaction &transaction);
44 // void createFolder(const QByteArray &rid, const QMap<QString, QVariant> &data, flatbuffers::FlatBufferBuilder &entityFbb, Akonadi2::Storage::Transaction &);
45 // void synchronize(const QString &bufferType, const QMap<QString, QMap<QString, QVariant> > &data, Akonadi2::Storage::Transaction &transaction, std::function<void(const QByteArray &ridBuffer, const QMap<QString, QVariant> &data, flatbuffers::FlatBufferBuilder &entityFbb, Akonadi2::Storage::Transaction &)> createEntity);
46 void synchronizeFolders(Akonadi2::Storage::Transaction &transaction); 44 void synchronizeFolders(Akonadi2::Storage::Transaction &transaction);
47 void synchronizeMails(Akonadi2::Storage::Transaction &transaction, const QString &folder); 45 void synchronizeMails(Akonadi2::Storage::Transaction &transaction, const QString &folder);
48 QStringList listAvailableFolders(); 46 QStringList listAvailableFolders();