From 1e2e4437094d80f1cdd849c7341019910fc29fb1 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Mon, 13 Jun 2016 09:11:37 +0200 Subject: Get folder moves to work, and fix the mime message moving. --- common/domain/applicationdomaintype.cpp | 2 + common/entityreader.cpp | 5 +- common/pipeline.cpp | 11 ++-- common/synchronizer.cpp | 3 +- examples/imapresource/imapresource.cpp | 34 +++++++---- tests/mailtest.cpp | 103 ++++++++++++++++++++++++++++---- tests/mailtest.h | 2 + 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 return; } file.write(value); + //Ensure that the file is written to disk immediately + file.close(); setProperty(key, path); } 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::EntityReader(const QByteArray &resourceType, const QBy mDomainTypeAdaptorFactoryPtr(Sink::AdaptorFactoryRegistry::instance().getFactory(resourceType)), mDomainTypeAdaptorFactory(*mDomainTypeAdaptorFactoryPtr) { - + Q_ASSERT(!resourceType.isEmpty()); + Trace() << "resourceType " << resourceType; + Q_ASSERT(mDomainTypeAdaptorFactoryPtr); } template @@ -159,6 +161,7 @@ void EntityReader::readEntity(const Sink::Storage::NamedDatabase &db const qint64 revision = metadataBuffer ? metadataBuffer->revision() : -1; const auto operation = metadataBuffer ? metadataBuffer->operation() : Sink::Operation_Creation; auto adaptor = mDomainTypeAdaptorFactory.createAdaptor(entity); + Q_ASSERT(adaptor); resultCallback(DomainType::Ptr::create(mResourceInstanceIdentifier, Sink::Storage::uidFromKey(key), revision, adaptor), operation); return false; }, 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 Pipeline::modifiedEntity(void const *command, size_t size) Trace() << "Pipeline: Modified Entity"; d->transactionItemCount++; - const qint64 newRevision = Storage::maxRevision(d->transaction) + 1; - { flatbuffers::Verifier verifyer(reinterpret_cast(command), size); if (!Commands::VerifyModifyEntityBuffer(verifyer)) { @@ -305,13 +303,18 @@ KAsync::Job Pipeline::modifiedEntity(void const *command, size_t size) } for (auto processor : d->processors[bufferType]) { - processor->modifiedEntity(key, newRevision, *current, *newAdaptor, d->transaction); + processor->resourceType = d->resourceType; + processor->pipeline = this; + processor->modifiedEntity(key, Storage::maxRevision(d->transaction) + 1, *current, *newAdaptor, d->transaction); } + //The maxRevision may have changed meanwhile if the entity created sub-entities + const qint64 newRevision = Storage::maxRevision(d->transaction) + 1; // Add metadata buffer flatbuffers::FlatBufferBuilder metadataFbb; { - auto modifiedProperties = BufferUtils::toVector(metadataFbb, changeset); + //We add availableProperties to account for the properties that have been changed by the preprocessors + auto modifiedProperties = BufferUtils::toVector(metadataFbb, changeset + newAdaptor->availableProperties()); auto metadataBuilder = MetadataBuilder(metadataFbb); metadataBuilder.add_revision(newRevision); 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 const auto sinkId = syncStore().resolveRemoteId(bufferType, remoteId); const auto found = mainDatabase.contains(sinkId); auto adaptorFactory = Sink::AdaptorFactoryRegistry::instance().getFactory(mResourceType, bufferType); + Q_ASSERT(adaptorFactory); if (!found) { Trace() << "Found a new entity: " << remoteId; createEntity( @@ -178,7 +179,7 @@ void Synchronizer::createOrModify(const QByteArray &bufferType, const QByteArray Sink::Query query; query.propertyFilter = mergeCriteria; bool merge = false; - Sink::EntityReader reader(mResourceInstanceIdentifier, mResourceType, transaction()); + Sink::EntityReader reader(mResourceType, mResourceInstanceIdentifier, transaction()); reader.query(query, [this, bufferType, remoteId, &merge](const DomainType &o) -> bool{ 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 sSpecialPurposeNames = specialPurposeNames(); class DraftsProcessor : public Sink::Preprocessor { public: - DraftsProcessor() {} + DraftsProcessor(const QByteArray &resourceType, const QByteArray &resourceInstanceIdentifier) : mResourceType(resourceType), mResourceInstanceIdentifier(resourceInstanceIdentifier) {} QByteArray ensureDraftsFolder(Sink::Storage::Transaction &transaction) { if (mDraftsFolder.isEmpty()) { //Try to find an existing drafts folder - Sink::EntityReader reader(mResourceInstanceIdentifier, mResourceType, transaction); + Sink::EntityReader reader(mResourceType, mResourceInstanceIdentifier, transaction); reader.query(Sink::Query().filter(Query::Comparator("drafts", Query::Comparator::Contains)), [this](const ApplicationDomain::Folder &f) -> bool{ mDraftsFolder = f.identifier(); @@ -125,8 +125,8 @@ public: } QByteArray mDraftsFolder; - QByteArray mResourceInstanceIdentifier; QByteArray mResourceType; + QByteArray mResourceInstanceIdentifier; }; class MailPropertyExtractor : public Sink::EntityPreprocessor @@ -142,14 +142,19 @@ public: Warning() << "Failed to open the file: " << mimeMessagePath; return; } - auto mapped = f.map(0, qMin((qint64)8000, f.size())); + if (!f.size()) { + Warning() << "The file is empty."; + return; + } + const auto mappedSize = qMin((qint64)8000, f.size()); + auto mapped = f.map(0, mappedSize); if (!mapped) { - Warning() << "Failed to map file"; + Warning() << "Failed to map the file: " << f.errorString(); return; } KMime::Message *msg = new KMime::Message; - msg->setHead(KMime::CRLFtoLF(QByteArray::fromRawData(reinterpret_cast(mapped), f.size()))); + msg->setHead(KMime::CRLFtoLF(QByteArray::fromRawData(reinterpret_cast(mapped), mappedSize))); msg->parse(); mail.setExtractedSubject(msg->subject(true)->asUnicodeString()); @@ -176,12 +181,21 @@ public: QString moveMessage(const QString &oldPath, const Sink::ApplicationDomain::Mail &mail) { - const auto directory = Sink::resourceStorageLocation(mResourceInstanceIdentifier) + "/" + mail.getFolder(); + const auto directory = Sink::resourceStorageLocation(mResourceInstanceIdentifier); const auto filePath = directory + "/" + mail.identifier(); if (oldPath != filePath) { - QDir().mkpath(directory); + if (!QDir().mkpath(directory)) { + Warning() << "Failed to create the directory: " << directory; + } QFile::remove(filePath); - QFile::rename(oldPath, filePath); + QFile origFile(oldPath); + if (!origFile.open(QIODevice::ReadWrite)) { + Warning() << "Failed to open the original file with write rights: " << origFile.errorString(); + } + if (!origFile.rename(filePath)) { + Warning() << "Failed to move the file from: " << oldPath << " to " << filePath << ". " << origFile.errorString(); + } + origFile.close(); return filePath; } return oldPath; @@ -633,7 +647,7 @@ ImapResource::ImapResource(const QByteArray &instanceIdentifier, const QSharedPo changereplay->mPassword = mPassword; setupChangereplay(changereplay); - setupPreprocessors(ENTITY_TYPE_MAIL, QVector() << new DraftsProcessor << new MimeMessageMover(mResourceInstanceIdentifier) << new MailPropertyExtractor << new DefaultIndexUpdater); + setupPreprocessors(ENTITY_TYPE_MAIL, QVector() << new DraftsProcessor(mResourceType, mResourceInstanceIdentifier) << new MimeMessageMover(mResourceInstanceIdentifier) << new MailPropertyExtractor << new DefaultIndexUpdater); setupPreprocessors(ENTITY_TYPE_FOLDER, QVector() << new DefaultIndexUpdater); } 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() VERIFYEXEC(Store::create(mail)); VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); + + Mail modifiedMail; { auto job = Store::fetchAll(Query::RequestedProperties(QByteArrayList() << Mail::Folder::name << Mail::Subject::name << Mail::MimeMessage::name)) - .then>([=](const QList &mails) { + .then>([=, &modifiedMail](const QList &mails) { QCOMPARE(mails.size(), 1); auto mail = *mails.first(); + modifiedMail = mail; QCOMPARE(mail.getFolder(), folder.identifier()); Warning() << "path: " << mail.getMimeMessagePath(); QVERIFY(QFile(mail.getMimeMessagePath()).exists()); @@ -256,9 +259,9 @@ void MailTest::testMoveMail() VERIFYEXEC(ResourceControl::inspect(ResourceControl::Inspection::CacheIntegrityInspection(folder))); - mail.setFolder(folder1); + modifiedMail.setFolder(folder1); - VERIFYEXEC(Store::modify(mail)); + VERIFYEXEC(Store::modify(modifiedMail)); VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); { auto job = Store::fetchAll(Query::RequestedProperties(QByteArrayList() << Mail::Folder::name << Mail::Subject::name << Mail::MimeMessage::name)) @@ -349,17 +352,93 @@ void MailTest::testCreateDraft() VERIFYEXEC(job); //Ensure we can also query by folder - auto job2 = Store::fetchAll(Query::PropertyFilter("folder", folderIdentifier)) - .then >([&](const QList &mails) { - bool found = false; - for (const auto m : mails) { - if (m->identifier() == mail.identifier()) { - found = true; + { + auto job = Store::fetchAll(Query::PropertyFilter("folder", folderIdentifier)) + .then >([&](const QList &mails) { + bool found = false; + for (const auto m : mails) { + if (m->identifier() == mail.identifier()) { + found = true; + } } - } - QVERIFY(found); + QVERIFY(found); + }); + VERIFYEXEC(job); + } + + //Ensure the folder is also existing + { + ApplicationDomain::Folder folder; + auto job = Store::fetchAll(Query::IdentityFilter(folderIdentifier)) + .then >([&](const QList &folders) { + QCOMPARE(folders.size(), 1); + folder = *folders.first(); + }); + VERIFYEXEC(job); + VERIFYEXEC(ResourceControl::inspect(ResourceControl::Inspection::ExistenceInspection(folder, true))); + } + VERIFYEXEC(ResourceControl::inspect(ResourceControl::Inspection::ExistenceInspection(mail, true))); +} + +void MailTest::testModifyMailToDraft() +{ + if (!mCapabilities.contains(ResourceCapabilities::Mail::drafts)) { + QSKIP("Resource doesn't have the drafts capability"); + } + + auto folder = Folder::create(mResourceInstanceIdentifier); + folder.setName("sdljldskjf"); + VERIFYEXEC(Store::create(folder)); + + auto message = KMime::Message::Ptr::create(); + message->subject(true)->fromUnicodeString(QString::fromLatin1("Foobar"), "utf8"); + message->assemble(); + + auto mail = ApplicationDomain::Mail::create(mResourceInstanceIdentifier); + mail.setMimeMessage(message->encodedContent()); + mail.setDraft(false); + mail.setFolder(folder); + + VERIFYEXEC(Store::create(mail)); + VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); + + ApplicationDomain::Mail modifiedMail; + { + auto job = Store::fetchOne(Query::IdentityFilter(mail.identifier()) + Query::RequestedProperties(QByteArrayList() << Mail::MimeMessage::name << Mail::Folder::name)) + .then([&](const ApplicationDomain::Mail &mail) { + modifiedMail = mail; + }); + VERIFYEXEC(job); + } + modifiedMail.setDraft(true); + VERIFYEXEC(Store::modify(modifiedMail)); + VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); + VERIFYEXEC(ResourceControl::flushReplayQueue(QByteArrayList() << mResourceInstanceIdentifier)); + + QByteArray folderIdentifier; + auto job = Store::fetchOne(Query::IdentityFilter(mail.identifier()) + Query::RequestedProperties(QByteArrayList() << Mail::MimeMessage::name << Mail::Folder::name)) + .then([&](const ApplicationDomain::Mail &mail) { + folderIdentifier = mail.getProperty("folder").toByteArray(); + QVERIFY(!folderIdentifier.isEmpty()); }); - VERIFYEXEC(job2); + VERIFYEXEC(job); + + //Ensure the folder is also existing + { + ApplicationDomain::Folder folder; + Query query; + query.ids << folderIdentifier; + query.request(); + auto job = Store::fetchAll(Query::IdentityFilter(folderIdentifier)) + .then >([&](const QList &folders) { + QCOMPARE(folders.size(), 1); + folder = *folders.first(); + QVERIFY(folder.getSpecialPurpose().contains("drafts")); + }); + VERIFYEXEC(job); + VERIFYEXEC(ResourceControl::inspect(ResourceControl::Inspection::ExistenceInspection(folder, true))); + } + VERIFYEXEC(ResourceControl::inspect(ResourceControl::Inspection::ExistenceInspection(mail, true))); } #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: void testCreateModifyDeleteMail(); void testMoveMail(); void testMarkMailAsRead(); + void testCreateDraft(); + void testModifyMailToDraft(); }; } -- cgit v1.2.3