From a908ea3ecb5ad78e4bdadf13d40ff76d0a038b76 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Sun, 19 Jul 2015 20:18:14 +0200 Subject: Modify/Delete actions --- common/entitybuffer.cpp | 7 ++++- common/entitybuffer.h | 3 ++- common/facade.h | 42 ++++++++++++++++++++++++++--- common/pipeline.cpp | 65 ++++++++++++++++++++++++++++++--------------- tests/dummyresourcetest.cpp | 41 ++++++++++++++++++++++++++++ 5 files changed, 131 insertions(+), 27 deletions(-) diff --git a/common/entitybuffer.cpp b/common/entitybuffer.cpp index 33a52fd..c14c86d 100644 --- a/common/entitybuffer.cpp +++ b/common/entitybuffer.cpp @@ -6,7 +6,7 @@ using namespace Akonadi2; -EntityBuffer::EntityBuffer(void *dataValue, int dataSize) +EntityBuffer::EntityBuffer(const void *dataValue, int dataSize) : mEntity(nullptr) { flatbuffers::Verifier verifyer(reinterpret_cast(dataValue), dataSize); @@ -18,6 +18,11 @@ EntityBuffer::EntityBuffer(void *dataValue, int dataSize) } } +bool EntityBuffer::isValid() const +{ + return mEntity; +} + const Akonadi2::Entity &EntityBuffer::entity() { return *mEntity; diff --git a/common/entitybuffer.h b/common/entitybuffer.h index dd7588e..a58aae9 100644 --- a/common/entitybuffer.h +++ b/common/entitybuffer.h @@ -8,11 +8,12 @@ struct Entity; class EntityBuffer { public: - EntityBuffer(void *dataValue, int size); + EntityBuffer(const void *dataValue, int size); const uint8_t *resourceBuffer(); const uint8_t *metadataBuffer(); const uint8_t *localBuffer(); const Entity &entity(); + bool isValid() const; static void extractResourceBuffer(void *dataValue, int dataSize, const std::function &handler); /* diff --git a/common/facade.h b/common/facade.h index abd4113..2ee7f42 100644 --- a/common/facade.h +++ b/common/facade.h @@ -28,6 +28,8 @@ #include "resourceaccess.h" #include "commands.h" #include "createentity_generated.h" +#include "modifyentity_generated.h" +#include "deleteentity_generated.h" #include "domainadaptor.h" #include "entitybuffer.h" #include "log.h" @@ -128,6 +130,7 @@ public: { if (!mDomainTypeAdaptorFactory) { Warning() << "No domain type adaptor factory available"; + return KAsync::error(); } flatbuffers::FlatBufferBuilder entityFbb; mDomainTypeAdaptorFactory->createBuffer(domainObject, entityFbb); @@ -136,14 +139,18 @@ public: KAsync::Job modify(const DomainType &domainObject) Q_DECL_OVERRIDE { - //TODO - return KAsync::null(); + if (!mDomainTypeAdaptorFactory) { + Warning() << "No domain type adaptor factory available"; + return KAsync::error(); + } + flatbuffers::FlatBufferBuilder entityFbb; + mDomainTypeAdaptorFactory->createBuffer(domainObject, entityFbb); + return sendModifyCommand(domainObject.identifier(), domainObject.revision(), bufferTypeForDomainType(), QByteArrayList(), QByteArray::fromRawData(reinterpret_cast(entityFbb.GetBufferPointer()), entityFbb.GetSize())); } KAsync::Job remove(const DomainType &domainObject) Q_DECL_OVERRIDE { - //TODO - return KAsync::null(); + return sendDeleteCommand(domainObject.identifier(), domainObject.revision(), bufferTypeForDomainType()); } //TODO JOBAPI return job from sync continuation to execute it as subjob? @@ -199,6 +206,33 @@ protected: return mResourceAccess->sendCommand(Akonadi2::Commands::CreateEntityCommand, fbb); } + KAsync::Job sendModifyCommand(const QByteArray &uid, qint64 revision, const QByteArray &resourceBufferType, const QByteArrayList &deletedProperties, const QByteArray &buffer) + { + flatbuffers::FlatBufferBuilder fbb; + auto entityId = fbb.CreateString(uid.constData()); + //This is the resource buffer type and not the domain type + auto type = fbb.CreateString(resourceBufferType.constData()); + //FIXME + auto deletions = 0; + auto delta = Akonadi2::EntityBuffer::appendAsVector(fbb, buffer.constData(), buffer.size()); + auto location = Akonadi2::Commands::CreateModifyEntity(fbb, revision, entityId, deletions, type, delta); + Akonadi2::Commands::FinishModifyEntityBuffer(fbb, location); + mResourceAccess->open(); + return mResourceAccess->sendCommand(Akonadi2::Commands::ModifyEntityCommand, fbb); + } + + KAsync::Job sendDeleteCommand(const QByteArray &uid, qint64 revision, const QByteArray &resourceBufferType) + { + flatbuffers::FlatBufferBuilder fbb; + auto entityId = fbb.CreateString(uid.constData()); + //This is the resource buffer type and not the domain type + auto type = fbb.CreateString(resourceBufferType.constData()); + auto location = Akonadi2::Commands::CreateDeleteEntity(fbb, revision, entityId, type); + Akonadi2::Commands::FinishDeleteEntityBuffer(fbb, location); + mResourceAccess->open(); + return mResourceAccess->sendCommand(Akonadi2::Commands::DeleteEntityCommand, fbb); + } + KAsync::Job synchronizeResource(bool sync, bool processAll) { //TODO check if a sync is necessary diff --git a/common/pipeline.cpp b/common/pipeline.cpp index afb9e34..1197408 100644 --- a/common/pipeline.cpp +++ b/common/pipeline.cpp @@ -84,13 +84,16 @@ void Pipeline::setPreprocessors(const QString &entityType, Type pipelineType, co }; } -Storage &Pipeline::storage() const void Pipeline::setAdaptorFactory(const QString &entityType, DomainTypeAdaptorFactoryInterface::Ptr factory) { - return d->storage; d->adaptorFactory.insert(entityType, factory); } +Storage &Pipeline::storage() const +{ + return d->storage; +} + void Pipeline::null() { //TODO: is there really any need for the null pipeline? if so, it should be doing something ;) @@ -111,7 +114,7 @@ KAsync::Job Pipeline::newEntity(void const *command, size_t size) { flatbuffers::Verifier verifyer(reinterpret_cast(command), size); if (!Akonadi2::Commands::VerifyCreateEntityBuffer(verifyer)) { - qWarning() << "invalid buffer, not a create entity buffer"; + Warning() << "invalid buffer, not a create entity buffer"; return KAsync::error(); } } @@ -122,7 +125,7 @@ KAsync::Job Pipeline::newEntity(void const *command, size_t size) { flatbuffers::Verifier verifyer(reinterpret_cast(createEntity->delta()->Data()), createEntity->delta()->size()); if (!Akonadi2::VerifyEntityBuffer(verifyer)) { - qWarning() << "invalid buffer, not an entity buffer"; + Warning() << "invalid buffer, not an entity buffer"; return KAsync::error(); } } @@ -142,7 +145,7 @@ KAsync::Job Pipeline::newEntity(void const *command, size_t size) storage().write(key.data(), key.size(), fbb.GetBufferPointer(), fbb.GetSize()); storage().setMaxRevision(newRevision); - Log() << "Pipeline: wrote entity: "<< newRevision; + Log() << "Pipeline: wrote entity: " << key << newRevision; return KAsync::start([this, key, entityType](KAsync::Future &future) { PipelineState state(this, NewPipeline, key, d->newPipeline[entityType], [&future]() { @@ -162,51 +165,72 @@ KAsync::Job Pipeline::modifiedEntity(void const *command, size_t size) { flatbuffers::Verifier verifyer(reinterpret_cast(command), size); if (!Akonadi2::Commands::VerifyModifyEntityBuffer(verifyer)) { - qWarning() << "invalid buffer, not a modify entity buffer"; + Warning() << "invalid buffer, not a modify entity buffer"; return KAsync::error(); } } auto modifyEntity = Akonadi2::Commands::GetModifyEntity(command); + Q_ASSERT(modifyEntity); //TODO rename modifyEntity->domainType to bufferType const QByteArray entityType = QByteArray(reinterpret_cast(modifyEntity->domainType()->Data()), modifyEntity->domainType()->size()); const QByteArray key = QByteArray(reinterpret_cast(modifyEntity->entityId()->Data()), modifyEntity->entityId()->size()); + if (entityType.isEmpty() || key.isEmpty()) { + Warning() << "entity type or key " << entityType << key; + return KAsync::error(); + } { flatbuffers::Verifier verifyer(reinterpret_cast(modifyEntity->delta()->Data()), modifyEntity->delta()->size()); if (!Akonadi2::VerifyEntityBuffer(verifyer)) { - qWarning() << "invalid buffer, not an entity buffer"; + Warning() << "invalid buffer, not an entity buffer"; return KAsync::error(); } } auto adaptorFactory = d->adaptorFactory.value(entityType); - if (adaptorFactory) { - qWarning() << "no adaptor factory"; + if (!adaptorFactory) { + Warning() << "no adaptor factory for type " << entityType; return KAsync::error(); } auto diffEntity = Akonadi2::GetEntity(modifyEntity->delta()->Data()); + Q_ASSERT(diffEntity); auto diff = adaptorFactory->createAdaptor(*diffEntity); - Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr domainType; - storage().scan(QByteArray::fromRawData(key.data(), key.size()), [&domainType](const QByteArray &data) -> bool { - auto existingEntity = Akonadi2::GetEntity(data.data()); - domainType = getDomainType(*existingEntity); + QSharedPointer current; + storage().scan(QByteArray::fromRawData(key.data(), key.size()), [¤t, adaptorFactory](const QByteArray &data) -> bool { + Akonadi2::EntityBuffer buffer(const_cast(data.data()), data.size()); + if (!buffer.isValid()) { + Warning() << "Read invalid buffer from disk"; + } else { + current = adaptorFactory->createAdaptor(buffer.entity()); + } return false; }); //TODO error handler + if (!current) { + Warning() << "Failed to read local value "; + return KAsync::error(); + } + + //resource and uid don't matter at this point + const Akonadi2::ApplicationDomain::ApplicationDomainType existingObject("", "", newRevision, current); + auto newObject = Akonadi2::ApplicationDomain::ApplicationDomainType::getInMemoryRepresentation(existingObject); + //Apply diff //FIXME only apply the properties that are available in the buffer for (const auto &property : diff->availableProperties()) { - domainType->setProperty(property, diff->getProperty(property)); + newObject->setProperty(property, diff->getProperty(property)); } + //Remove deletions - for (const auto &property : *modifyEntity->deletions()) { - domainType->setProperty(QByteArray::fromRawData(property->data(), property->size()), QVariant()); + if (modifyEntity->deletions()) { + for (const auto &property : *modifyEntity->deletions()) { + newObject->setProperty(QByteArray::fromRawData(property->data(), property->size()), QVariant()); + } } - //Add metadata buffer flatbuffers::FlatBufferBuilder metadataFbb; auto metadataBuilder = Akonadi2::MetadataBuilder(metadataFbb); @@ -215,16 +239,15 @@ KAsync::Job Pipeline::modifiedEntity(void const *command, size_t size) auto metadataBuffer = metadataBuilder.Finish(); Akonadi2::FinishMetadataBuffer(metadataFbb, metadataBuffer); - flatbuffers::FlatBufferBuilder fbb; - adaptorFactory->createBuffer(*domainType, fbb, metadataFbb.GetBufferPointer(), metadataFbb.GetSize()); + adaptorFactory->createBuffer(*newObject, fbb, metadataFbb.GetBufferPointer(), metadataFbb.GetSize()); //TODO don't overwrite the old entry, but instead store a new revision storage().write(key.data(), key.size(), fbb.GetBufferPointer(), fbb.GetSize()); storage().setMaxRevision(newRevision); return KAsync::start([this, key, entityType](KAsync::Future &future) { - PipelineState state(this, ModifiedPipeline, key, d->newPipeline[entityType], [&future]() { + PipelineState state(this, ModifiedPipeline, key, d->modifiedPipeline[entityType], [&future]() { future.setFinished(); }); d->activePipelines << state; @@ -241,7 +264,7 @@ KAsync::Job Pipeline::deletedEntity(void const *command, size_t size) { flatbuffers::Verifier verifyer(reinterpret_cast(command), size); if (!Akonadi2::Commands::VerifyDeleteEntityBuffer(verifyer)) { - qWarning() << "invalid buffer, not a delete entity buffer"; + Warning() << "invalid buffer, not a delete entity buffer"; return KAsync::error(); } } diff --git a/tests/dummyresourcetest.cpp b/tests/dummyresourcetest.cpp index 49558f8..7499d62 100644 --- a/tests/dummyresourcetest.cpp +++ b/tests/dummyresourcetest.cpp @@ -208,6 +208,47 @@ private Q_SLOTS: qDebug() << value->getProperty("summary").toString(); } + void testWriteModifyDelete() + { + Akonadi2::ApplicationDomain::Event event; + event.setProperty("uid", "testuid"); + QCOMPARE(event.getProperty("uid").toByteArray(), QByteArray("testuid")); + event.setProperty("summary", "summaryValue"); + Akonadi2::Store::create(event, "org.kde.dummy.instance1"); + + Akonadi2::Query query; + query.resources << "org.kde.dummy.instance1"; + query.syncOnDemand = false; + query.processAll = true; + query.propertyFilter.insert("uid", "testuid"); + + //Test create + Akonadi2::ApplicationDomain::Event event2; + { + async::SyncListResult result(Akonadi2::Store::load(query)); + result.exec(); + QCOMPARE(result.size(), 1); + auto value = result.first(); + QCOMPARE(value->getProperty("uid").toByteArray(), QByteArray("testuid")); + QCOMPARE(value->getProperty("summary").toByteArray(), QByteArray("summaryValue")); + event2 = *value; + } + + event2.setProperty("uid", "testuid"); + event2.setProperty("summary", "summaryValue2"); + Akonadi2::Store::modify(event2, "org.kde.dummy.instance1"); + + //Test modify + { + async::SyncListResult result(Akonadi2::Store::load(query)); + result.exec(); + QCOMPARE(result.size(), 1); + auto value = result.first(); + QCOMPARE(value->getProperty("uid").toByteArray(), QByteArray("testuid")); + QCOMPARE(value->getProperty("summary").toByteArray(), QByteArray("summaryValue2")); + } + } + }; QTEST_MAIN(DummyResourceTest) -- cgit v1.2.3