summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/domain/applicationdomaintype.cpp2
-rw-r--r--common/entityreader.cpp5
-rw-r--r--common/pipeline.cpp11
-rw-r--r--common/synchronizer.cpp3
-rw-r--r--examples/imapresource/imapresource.cpp34
-rw-r--r--tests/mailtest.cpp103
-rw-r--r--tests/mailtest.h2
7 files changed, 132 insertions, 28 deletions
diff --git a/common/domain/applicationdomaintype.cpp b/common/domain/applicationdomaintype.cpp
index d9b0c92..4db2e3b 100644
--- a/common/domain/applicationdomaintype.cpp
+++ b/common/domain/applicationdomaintype.cpp
@@ -120,6 +120,8 @@ void ApplicationDomainType::setBlobProperty(const QByteArray &key, const QByteAr
120 return; 120 return;
121 } 121 }
122 file.write(value); 122 file.write(value);
123 //Ensure that the file is written to disk immediately
124 file.close();
123 setProperty(key, path); 125 setProperty(key, path);
124} 126}
125 127
diff --git a/common/entityreader.cpp b/common/entityreader.cpp
index b29b2a3..a3ca8e2 100644
--- a/common/entityreader.cpp
+++ b/common/entityreader.cpp
@@ -85,7 +85,9 @@ EntityReader<DomainType>::EntityReader(const QByteArray &resourceType, const QBy
85 mDomainTypeAdaptorFactoryPtr(Sink::AdaptorFactoryRegistry::instance().getFactory<DomainType>(resourceType)), 85 mDomainTypeAdaptorFactoryPtr(Sink::AdaptorFactoryRegistry::instance().getFactory<DomainType>(resourceType)),
86 mDomainTypeAdaptorFactory(*mDomainTypeAdaptorFactoryPtr) 86 mDomainTypeAdaptorFactory(*mDomainTypeAdaptorFactoryPtr)
87{ 87{
88 88 Q_ASSERT(!resourceType.isEmpty());
89 Trace() << "resourceType " << resourceType;
90 Q_ASSERT(mDomainTypeAdaptorFactoryPtr);
89} 91}
90 92
91template <class DomainType> 93template <class DomainType>
@@ -159,6 +161,7 @@ void EntityReader<DomainType>::readEntity(const Sink::Storage::NamedDatabase &db
159 const qint64 revision = metadataBuffer ? metadataBuffer->revision() : -1; 161 const qint64 revision = metadataBuffer ? metadataBuffer->revision() : -1;
160 const auto operation = metadataBuffer ? metadataBuffer->operation() : Sink::Operation_Creation; 162 const auto operation = metadataBuffer ? metadataBuffer->operation() : Sink::Operation_Creation;
161 auto adaptor = mDomainTypeAdaptorFactory.createAdaptor(entity); 163 auto adaptor = mDomainTypeAdaptorFactory.createAdaptor(entity);
164 Q_ASSERT(adaptor);
162 resultCallback(DomainType::Ptr::create(mResourceInstanceIdentifier, Sink::Storage::uidFromKey(key), revision, adaptor), operation); 165 resultCallback(DomainType::Ptr::create(mResourceInstanceIdentifier, Sink::Storage::uidFromKey(key), revision, adaptor), operation);
163 return false; 166 return false;
164 }, 167 },
diff --git a/common/pipeline.cpp b/common/pipeline.cpp
index 2c08aa0..5d8a34c 100644
--- a/common/pipeline.cpp
+++ b/common/pipeline.cpp
@@ -221,8 +221,6 @@ KAsync::Job<qint64> Pipeline::modifiedEntity(void const *command, size_t size)
221 Trace() << "Pipeline: Modified Entity"; 221 Trace() << "Pipeline: Modified Entity";
222 d->transactionItemCount++; 222 d->transactionItemCount++;
223 223
224 const qint64 newRevision = Storage::maxRevision(d->transaction) + 1;
225
226 { 224 {
227 flatbuffers::Verifier verifyer(reinterpret_cast<const uint8_t *>(command), size); 225 flatbuffers::Verifier verifyer(reinterpret_cast<const uint8_t *>(command), size);
228 if (!Commands::VerifyModifyEntityBuffer(verifyer)) { 226 if (!Commands::VerifyModifyEntityBuffer(verifyer)) {
@@ -305,13 +303,18 @@ KAsync::Job<qint64> Pipeline::modifiedEntity(void const *command, size_t size)
305 } 303 }
306 304
307 for (auto processor : d->processors[bufferType]) { 305 for (auto processor : d->processors[bufferType]) {
308 processor->modifiedEntity(key, newRevision, *current, *newAdaptor, d->transaction); 306 processor->resourceType = d->resourceType;
307 processor->pipeline = this;
308 processor->modifiedEntity(key, Storage::maxRevision(d->transaction) + 1, *current, *newAdaptor, d->transaction);
309 } 309 }
310 //The maxRevision may have changed meanwhile if the entity created sub-entities
311 const qint64 newRevision = Storage::maxRevision(d->transaction) + 1;
310 312
311 // Add metadata buffer 313 // Add metadata buffer
312 flatbuffers::FlatBufferBuilder metadataFbb; 314 flatbuffers::FlatBufferBuilder metadataFbb;
313 { 315 {
314 auto modifiedProperties = BufferUtils::toVector(metadataFbb, changeset); 316 //We add availableProperties to account for the properties that have been changed by the preprocessors
317 auto modifiedProperties = BufferUtils::toVector(metadataFbb, changeset + newAdaptor->availableProperties());
315 auto metadataBuilder = MetadataBuilder(metadataFbb); 318 auto metadataBuilder = MetadataBuilder(metadataFbb);
316 metadataBuilder.add_revision(newRevision); 319 metadataBuilder.add_revision(newRevision);
317 metadataBuilder.add_operation(Operation_Modification); 320 metadataBuilder.add_operation(Operation_Modification);
diff --git a/common/synchronizer.cpp b/common/synchronizer.cpp
index b127ec5..46712b5 100644
--- a/common/synchronizer.cpp
+++ b/common/synchronizer.cpp
@@ -139,6 +139,7 @@ void Synchronizer::createOrModify(const QByteArray &bufferType, const QByteArray
139 const auto sinkId = syncStore().resolveRemoteId(bufferType, remoteId); 139 const auto sinkId = syncStore().resolveRemoteId(bufferType, remoteId);
140 const auto found = mainDatabase.contains(sinkId); 140 const auto found = mainDatabase.contains(sinkId);
141 auto adaptorFactory = Sink::AdaptorFactoryRegistry::instance().getFactory(mResourceType, bufferType); 141 auto adaptorFactory = Sink::AdaptorFactoryRegistry::instance().getFactory(mResourceType, bufferType);
142 Q_ASSERT(adaptorFactory);
142 if (!found) { 143 if (!found) {
143 Trace() << "Found a new entity: " << remoteId; 144 Trace() << "Found a new entity: " << remoteId;
144 createEntity( 145 createEntity(
@@ -178,7 +179,7 @@ void Synchronizer::createOrModify(const QByteArray &bufferType, const QByteArray
178 Sink::Query query; 179 Sink::Query query;
179 query.propertyFilter = mergeCriteria; 180 query.propertyFilter = mergeCriteria;
180 bool merge = false; 181 bool merge = false;
181 Sink::EntityReader<DomainType> reader(mResourceInstanceIdentifier, mResourceType, transaction()); 182 Sink::EntityReader<DomainType> reader(mResourceType, mResourceInstanceIdentifier, transaction());
182 reader.query(query, 183 reader.query(query,
183 [this, bufferType, remoteId, &merge](const DomainType &o) -> bool{ 184 [this, bufferType, remoteId, &merge](const DomainType &o) -> bool{
184 merge = true; 185 merge = true;
diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp
index 8cbb9fa..b3aafed 100644
--- a/examples/imapresource/imapresource.cpp
+++ b/examples/imapresource/imapresource.cpp
@@ -83,13 +83,13 @@ static QHash<QString, QByteArray> sSpecialPurposeNames = specialPurposeNames();
83class DraftsProcessor : public Sink::Preprocessor 83class DraftsProcessor : public Sink::Preprocessor
84{ 84{
85public: 85public:
86 DraftsProcessor() {} 86 DraftsProcessor(const QByteArray &resourceType, const QByteArray &resourceInstanceIdentifier) : mResourceType(resourceType), mResourceInstanceIdentifier(resourceInstanceIdentifier) {}
87 87
88 QByteArray ensureDraftsFolder(Sink::Storage::Transaction &transaction) 88 QByteArray ensureDraftsFolder(Sink::Storage::Transaction &transaction)
89 { 89 {
90 if (mDraftsFolder.isEmpty()) { 90 if (mDraftsFolder.isEmpty()) {
91 //Try to find an existing drafts folder 91 //Try to find an existing drafts folder
92 Sink::EntityReader<ApplicationDomain::Folder> reader(mResourceInstanceIdentifier, mResourceType, transaction); 92 Sink::EntityReader<ApplicationDomain::Folder> reader(mResourceType, mResourceInstanceIdentifier, transaction);
93 reader.query(Sink::Query().filter<ApplicationDomain::Folder::SpecialPurpose>(Query::Comparator("drafts", Query::Comparator::Contains)), 93 reader.query(Sink::Query().filter<ApplicationDomain::Folder::SpecialPurpose>(Query::Comparator("drafts", Query::Comparator::Contains)),
94 [this](const ApplicationDomain::Folder &f) -> bool{ 94 [this](const ApplicationDomain::Folder &f) -> bool{
95 mDraftsFolder = f.identifier(); 95 mDraftsFolder = f.identifier();
@@ -125,8 +125,8 @@ public:
125 } 125 }
126 126
127 QByteArray mDraftsFolder; 127 QByteArray mDraftsFolder;
128 QByteArray mResourceInstanceIdentifier;
129 QByteArray mResourceType; 128 QByteArray mResourceType;
129 QByteArray mResourceInstanceIdentifier;
130}; 130};
131 131
132class MailPropertyExtractor : public Sink::EntityPreprocessor<ApplicationDomain::Mail> 132class MailPropertyExtractor : public Sink::EntityPreprocessor<ApplicationDomain::Mail>
@@ -142,14 +142,19 @@ public:
142 Warning() << "Failed to open the file: " << mimeMessagePath; 142 Warning() << "Failed to open the file: " << mimeMessagePath;
143 return; 143 return;
144 } 144 }
145 auto mapped = f.map(0, qMin((qint64)8000, f.size())); 145 if (!f.size()) {
146 Warning() << "The file is empty.";
147 return;
148 }
149 const auto mappedSize = qMin((qint64)8000, f.size());
150 auto mapped = f.map(0, mappedSize);
146 if (!mapped) { 151 if (!mapped) {
147 Warning() << "Failed to map file"; 152 Warning() << "Failed to map the file: " << f.errorString();
148 return; 153 return;
149 } 154 }
150 155
151 KMime::Message *msg = new KMime::Message; 156 KMime::Message *msg = new KMime::Message;
152 msg->setHead(KMime::CRLFtoLF(QByteArray::fromRawData(reinterpret_cast<const char*>(mapped), f.size()))); 157 msg->setHead(KMime::CRLFtoLF(QByteArray::fromRawData(reinterpret_cast<const char*>(mapped), mappedSize)));
153 msg->parse(); 158 msg->parse();
154 159
155 mail.setExtractedSubject(msg->subject(true)->asUnicodeString()); 160 mail.setExtractedSubject(msg->subject(true)->asUnicodeString());
@@ -176,12 +181,21 @@ public:
176 181
177 QString moveMessage(const QString &oldPath, const Sink::ApplicationDomain::Mail &mail) 182 QString moveMessage(const QString &oldPath, const Sink::ApplicationDomain::Mail &mail)
178 { 183 {
179 const auto directory = Sink::resourceStorageLocation(mResourceInstanceIdentifier) + "/" + mail.getFolder(); 184 const auto directory = Sink::resourceStorageLocation(mResourceInstanceIdentifier);
180 const auto filePath = directory + "/" + mail.identifier(); 185 const auto filePath = directory + "/" + mail.identifier();
181 if (oldPath != filePath) { 186 if (oldPath != filePath) {
182 QDir().mkpath(directory); 187 if (!QDir().mkpath(directory)) {
188 Warning() << "Failed to create the directory: " << directory;
189 }
183 QFile::remove(filePath); 190 QFile::remove(filePath);
184 QFile::rename(oldPath, filePath); 191 QFile origFile(oldPath);
192 if (!origFile.open(QIODevice::ReadWrite)) {
193 Warning() << "Failed to open the original file with write rights: " << origFile.errorString();
194 }
195 if (!origFile.rename(filePath)) {
196 Warning() << "Failed to move the file from: " << oldPath << " to " << filePath << ". " << origFile.errorString();
197 }
198 origFile.close();
185 return filePath; 199 return filePath;
186 } 200 }
187 return oldPath; 201 return oldPath;
@@ -633,7 +647,7 @@ ImapResource::ImapResource(const QByteArray &instanceIdentifier, const QSharedPo
633 changereplay->mPassword = mPassword; 647 changereplay->mPassword = mPassword;
634 setupChangereplay(changereplay); 648 setupChangereplay(changereplay);
635 649
636 setupPreprocessors(ENTITY_TYPE_MAIL, QVector<Sink::Preprocessor*>() << new DraftsProcessor << new MimeMessageMover(mResourceInstanceIdentifier) << new MailPropertyExtractor << new DefaultIndexUpdater<Sink::ApplicationDomain::Mail>); 650 setupPreprocessors(ENTITY_TYPE_MAIL, QVector<Sink::Preprocessor*>() << new DraftsProcessor(mResourceType, mResourceInstanceIdentifier) << new MimeMessageMover(mResourceInstanceIdentifier) << new MailPropertyExtractor << new DefaultIndexUpdater<Sink::ApplicationDomain::Mail>);
637 setupPreprocessors(ENTITY_TYPE_FOLDER, QVector<Sink::Preprocessor*>() << new DefaultIndexUpdater<Sink::ApplicationDomain::Folder>); 651 setupPreprocessors(ENTITY_TYPE_FOLDER, QVector<Sink::Preprocessor*>() << new DefaultIndexUpdater<Sink::ApplicationDomain::Folder>);
638} 652}
639 653
diff --git a/tests/mailtest.cpp b/tests/mailtest.cpp
index 2fcad93..4e36517 100644
--- a/tests/mailtest.cpp
+++ b/tests/mailtest.cpp
@@ -242,11 +242,14 @@ void MailTest::testMoveMail()
242 242
243 VERIFYEXEC(Store::create(mail)); 243 VERIFYEXEC(Store::create(mail));
244 VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); 244 VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier));
245
246 Mail modifiedMail;
245 { 247 {
246 auto job = Store::fetchAll<Mail>(Query::RequestedProperties(QByteArrayList() << Mail::Folder::name << Mail::Subject::name << Mail::MimeMessage::name)) 248 auto job = Store::fetchAll<Mail>(Query::RequestedProperties(QByteArrayList() << Mail::Folder::name << Mail::Subject::name << Mail::MimeMessage::name))
247 .then<void, QList<Mail::Ptr>>([=](const QList<Mail::Ptr> &mails) { 249 .then<void, QList<Mail::Ptr>>([=, &modifiedMail](const QList<Mail::Ptr> &mails) {
248 QCOMPARE(mails.size(), 1); 250 QCOMPARE(mails.size(), 1);
249 auto mail = *mails.first(); 251 auto mail = *mails.first();
252 modifiedMail = mail;
250 QCOMPARE(mail.getFolder(), folder.identifier()); 253 QCOMPARE(mail.getFolder(), folder.identifier());
251 Warning() << "path: " << mail.getMimeMessagePath(); 254 Warning() << "path: " << mail.getMimeMessagePath();
252 QVERIFY(QFile(mail.getMimeMessagePath()).exists()); 255 QVERIFY(QFile(mail.getMimeMessagePath()).exists());
@@ -256,9 +259,9 @@ void MailTest::testMoveMail()
256 259
257 VERIFYEXEC(ResourceControl::inspect<ApplicationDomain::Folder>(ResourceControl::Inspection::CacheIntegrityInspection(folder))); 260 VERIFYEXEC(ResourceControl::inspect<ApplicationDomain::Folder>(ResourceControl::Inspection::CacheIntegrityInspection(folder)));
258 261
259 mail.setFolder(folder1); 262 modifiedMail.setFolder(folder1);
260 263
261 VERIFYEXEC(Store::modify(mail)); 264 VERIFYEXEC(Store::modify(modifiedMail));
262 VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); 265 VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier));
263 { 266 {
264 auto job = Store::fetchAll<Mail>(Query::RequestedProperties(QByteArrayList() << Mail::Folder::name << Mail::Subject::name << Mail::MimeMessage::name)) 267 auto job = Store::fetchAll<Mail>(Query::RequestedProperties(QByteArrayList() << Mail::Folder::name << Mail::Subject::name << Mail::MimeMessage::name))
@@ -349,17 +352,93 @@ void MailTest::testCreateDraft()
349 VERIFYEXEC(job); 352 VERIFYEXEC(job);
350 353
351 //Ensure we can also query by folder 354 //Ensure we can also query by folder
352 auto job2 = Store::fetchAll<ApplicationDomain::Mail>(Query::PropertyFilter("folder", folderIdentifier)) 355 {
353 .then<void, QList<ApplicationDomain::Mail::Ptr> >([&](const QList<ApplicationDomain::Mail::Ptr> &mails) { 356 auto job = Store::fetchAll<ApplicationDomain::Mail>(Query::PropertyFilter("folder", folderIdentifier))
354 bool found = false; 357 .then<void, QList<ApplicationDomain::Mail::Ptr> >([&](const QList<ApplicationDomain::Mail::Ptr> &mails) {
355 for (const auto m : mails) { 358 bool found = false;
356 if (m->identifier() == mail.identifier()) { 359 for (const auto m : mails) {
357 found = true; 360 if (m->identifier() == mail.identifier()) {
361 found = true;
362 }
358 } 363 }
359 } 364 QVERIFY(found);
360 QVERIFY(found); 365 });
366 VERIFYEXEC(job);
367 }
368
369 //Ensure the folder is also existing
370 {
371 ApplicationDomain::Folder folder;
372 auto job = Store::fetchAll<ApplicationDomain::Folder>(Query::IdentityFilter(folderIdentifier))
373 .then<void, QList<ApplicationDomain::Folder::Ptr> >([&](const QList<ApplicationDomain::Folder::Ptr> &folders) {
374 QCOMPARE(folders.size(), 1);
375 folder = *folders.first();
376 });
377 VERIFYEXEC(job);
378 VERIFYEXEC(ResourceControl::inspect<ApplicationDomain::Folder>(ResourceControl::Inspection::ExistenceInspection(folder, true)));
379 }
380 VERIFYEXEC(ResourceControl::inspect<ApplicationDomain::Mail>(ResourceControl::Inspection::ExistenceInspection(mail, true)));
381}
382
383void MailTest::testModifyMailToDraft()
384{
385 if (!mCapabilities.contains(ResourceCapabilities::Mail::drafts)) {
386 QSKIP("Resource doesn't have the drafts capability");
387 }
388
389 auto folder = Folder::create(mResourceInstanceIdentifier);
390 folder.setName("sdljldskjf");
391 VERIFYEXEC(Store::create(folder));
392
393 auto message = KMime::Message::Ptr::create();
394 message->subject(true)->fromUnicodeString(QString::fromLatin1("Foobar"), "utf8");
395 message->assemble();
396
397 auto mail = ApplicationDomain::Mail::create(mResourceInstanceIdentifier);
398 mail.setMimeMessage(message->encodedContent());
399 mail.setDraft(false);
400 mail.setFolder(folder);
401
402 VERIFYEXEC(Store::create(mail));
403 VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier));
404
405 ApplicationDomain::Mail modifiedMail;
406 {
407 auto job = Store::fetchOne<ApplicationDomain::Mail>(Query::IdentityFilter(mail.identifier()) + Query::RequestedProperties(QByteArrayList() << Mail::MimeMessage::name << Mail::Folder::name))
408 .then<void, ApplicationDomain::Mail>([&](const ApplicationDomain::Mail &mail) {
409 modifiedMail = mail;
410 });
411 VERIFYEXEC(job);
412 }
413 modifiedMail.setDraft(true);
414 VERIFYEXEC(Store::modify(modifiedMail));
415 VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier));
416 VERIFYEXEC(ResourceControl::flushReplayQueue(QByteArrayList() << mResourceInstanceIdentifier));
417
418 QByteArray folderIdentifier;
419 auto job = Store::fetchOne<ApplicationDomain::Mail>(Query::IdentityFilter(mail.identifier()) + Query::RequestedProperties(QByteArrayList() << Mail::MimeMessage::name << Mail::Folder::name))
420 .then<void, ApplicationDomain::Mail>([&](const ApplicationDomain::Mail &mail) {
421 folderIdentifier = mail.getProperty("folder").toByteArray();
422 QVERIFY(!folderIdentifier.isEmpty());
361 }); 423 });
362 VERIFYEXEC(job2); 424 VERIFYEXEC(job);
425
426 //Ensure the folder is also existing
427 {
428 ApplicationDomain::Folder folder;
429 Query query;
430 query.ids << folderIdentifier;
431 query.request<Folder::SpecialPurpose>();
432 auto job = Store::fetchAll<ApplicationDomain::Folder>(Query::IdentityFilter(folderIdentifier))
433 .then<void, QList<ApplicationDomain::Folder::Ptr> >([&](const QList<ApplicationDomain::Folder::Ptr> &folders) {
434 QCOMPARE(folders.size(), 1);
435 folder = *folders.first();
436 QVERIFY(folder.getSpecialPurpose().contains("drafts"));
437 });
438 VERIFYEXEC(job);
439 VERIFYEXEC(ResourceControl::inspect<ApplicationDomain::Folder>(ResourceControl::Inspection::ExistenceInspection(folder, true)));
440 }
441 VERIFYEXEC(ResourceControl::inspect<ApplicationDomain::Mail>(ResourceControl::Inspection::ExistenceInspection(mail, true)));
363} 442}
364 443
365#include "mailtest.moc" 444#include "mailtest.moc"
diff --git a/tests/mailtest.h b/tests/mailtest.h
index 3ba7f63..3de5e1f 100644
--- a/tests/mailtest.h
+++ b/tests/mailtest.h
@@ -66,7 +66,9 @@ private slots:
66 void testCreateModifyDeleteMail(); 66 void testCreateModifyDeleteMail();
67 void testMoveMail(); 67 void testMoveMail();
68 void testMarkMailAsRead(); 68 void testMarkMailAsRead();
69
69 void testCreateDraft(); 70 void testCreateDraft();
71 void testModifyMailToDraft();
70}; 72};
71 73
72} 74}