summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/CMakeLists.txt1
-rw-r--r--common/clientapi.h2
-rw-r--r--common/commands/modifyentity.fbs4
-rw-r--r--common/entitybuffer.cpp1
-rw-r--r--common/resourceaccess.cpp2
-rw-r--r--common/storage_lmdb.cpp5
-rw-r--r--dummyresource/domainadaptor.cpp26
-rw-r--r--dummyresource/facade.cpp145
-rw-r--r--dummyresource/facade.h1
-rw-r--r--dummyresource/resourcefactory.cpp26
-rw-r--r--tests/dummyresourcetest.cpp40
11 files changed, 184 insertions, 69 deletions
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index 6f8fee3..3d3a2b7 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -32,6 +32,7 @@ set(command_SRCS
32 storage_common.cpp 32 storage_common.cpp
33 threadboundary.cpp 33 threadboundary.cpp
34 messagequeue.cpp 34 messagequeue.cpp
35 index.cpp
35 ${storage_SRCS}) 36 ${storage_SRCS})
36 37
37add_library(${PROJECT_NAME} SHARED ${command_SRCS}) 38add_library(${PROJECT_NAME} SHARED ${command_SRCS})
diff --git a/common/clientapi.h b/common/clientapi.h
index 546f210..659ae91 100644
--- a/common/clientapi.h
+++ b/common/clientapi.h
@@ -228,7 +228,7 @@ public:
228 } 228 }
229 229
230 virtual QVariant getProperty(const QString &key) const { return mAdaptor->getProperty(key); } 230 virtual QVariant getProperty(const QString &key) const { return mAdaptor->getProperty(key); }
231 virtual void setProperty(const QString &key, const QVariant &value){ mChangeSet.insert(key, value); } 231 virtual void setProperty(const QString &key, const QVariant &value){ mChangeSet.insert(key, value); mAdaptor->setProperty(key, value); }
232 232
233private: 233private:
234 QSharedPointer<BufferAdaptor> mAdaptor; 234 QSharedPointer<BufferAdaptor> mAdaptor;
diff --git a/common/commands/modifyentity.fbs b/common/commands/modifyentity.fbs
index b4edb12..d26051e 100644
--- a/common/commands/modifyentity.fbs
+++ b/common/commands/modifyentity.fbs
@@ -3,9 +3,9 @@ namespace Akonadi2;
3table ModifyEntity { 3table ModifyEntity {
4 revision: ulong; 4 revision: ulong;
5 entityId: string; 5 entityId: string;
6 deletions: [string]; 6 deletions: [string]; //A list of deleted properties
7 domainType: string; 7 domainType: string;
8 delta: [ubyte]; 8 delta: [ubyte]; //Contains an entity buffer with all changed properties set
9} 9}
10 10
11root_type ModifyEntity; 11root_type ModifyEntity;
diff --git a/common/entitybuffer.cpp b/common/entitybuffer.cpp
index 4af84ef..5ba4afe 100644
--- a/common/entitybuffer.cpp
+++ b/common/entitybuffer.cpp
@@ -58,7 +58,6 @@ void EntityBuffer::extractResourceBuffer(void *dataValue, int dataSize, const st
58 58
59void EntityBuffer::assembleEntityBuffer(flatbuffers::FlatBufferBuilder &fbb, void const *metadataData, size_t metadataSize, void const *resourceData, size_t resourceSize, void const *localData, size_t localSize) 59void EntityBuffer::assembleEntityBuffer(flatbuffers::FlatBufferBuilder &fbb, void const *metadataData, size_t metadataSize, void const *resourceData, size_t resourceSize, void const *localData, size_t localSize)
60{ 60{
61 qDebug() << "res size: " << resourceSize;
62 auto metadata = fbb.CreateVector<uint8_t>(static_cast<uint8_t const*>(metadataData), metadataSize); 61 auto metadata = fbb.CreateVector<uint8_t>(static_cast<uint8_t const*>(metadataData), metadataSize);
63 auto resource = fbb.CreateVector<uint8_t>(static_cast<uint8_t const*>(resourceData), resourceSize); 62 auto resource = fbb.CreateVector<uint8_t>(static_cast<uint8_t const*>(resourceData), resourceSize);
64 auto local = fbb.CreateVector<uint8_t>(static_cast<uint8_t const*>(localData), localSize); 63 auto local = fbb.CreateVector<uint8_t>(static_cast<uint8_t const*>(localData), localSize);
diff --git a/common/resourceaccess.cpp b/common/resourceaccess.cpp
index 5f04db8..73a01ca 100644
--- a/common/resourceaccess.cpp
+++ b/common/resourceaccess.cpp
@@ -298,6 +298,7 @@ bool ResourceAccess::processMessageBuffer()
298{ 298{
299 static const int headerSize = Commands::headerSize(); 299 static const int headerSize = Commands::headerSize();
300 if (d->partialMessageBuffer.size() < headerSize) { 300 if (d->partialMessageBuffer.size() < headerSize) {
301 qWarning() << "command too small";
301 return false; 302 return false;
302 } 303 }
303 304
@@ -306,6 +307,7 @@ bool ResourceAccess::processMessageBuffer()
306 const uint size = *(int*)(d->partialMessageBuffer.constData() + sizeof(int) + sizeof(uint)); 307 const uint size = *(int*)(d->partialMessageBuffer.constData() + sizeof(int) + sizeof(uint));
307 308
308 if (size > (uint)(d->partialMessageBuffer.size() - headerSize)) { 309 if (size > (uint)(d->partialMessageBuffer.size() - headerSize)) {
310 qWarning() << "command too small";
309 return false; 311 return false;
310 } 312 }
311 313
diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp
index bede6aa..b7fbfed 100644
--- a/common/storage_lmdb.cpp
+++ b/common/storage_lmdb.cpp
@@ -218,6 +218,11 @@ bool Storage::write(void const *keyPtr, size_t keySize, void const *valuePtr, si
218 return false; 218 return false;
219 } 219 }
220 220
221 if (!keyPtr || keySize == 0) {
222 std::cerr << "tried to write empty key." << std::endl;
223 return false;
224 }
225
221 const bool implicitTransaction = !d->transaction || d->readTransaction; 226 const bool implicitTransaction = !d->transaction || d->readTransaction;
222 if (implicitTransaction) { 227 if (implicitTransaction) {
223 if (!startTransaction()) { 228 if (!startTransaction()) {
diff --git a/dummyresource/domainadaptor.cpp b/dummyresource/domainadaptor.cpp
index ec5e2be..d902052 100644
--- a/dummyresource/domainadaptor.cpp
+++ b/dummyresource/domainadaptor.cpp
@@ -40,9 +40,10 @@ public:
40 { 40 {
41 if (mResourceBuffer && mResourceMapper->mReadAccessors.contains(key)) { 41 if (mResourceBuffer && mResourceMapper->mReadAccessors.contains(key)) {
42 return mResourceMapper->getProperty(key, mResourceBuffer); 42 return mResourceMapper->getProperty(key, mResourceBuffer);
43 } else if (mLocalBuffer) { 43 } else if (mLocalBuffer && mLocalMapper->mReadAccessors.contains(key)) {
44 return mLocalMapper->getProperty(key, mLocalBuffer); 44 return mLocalMapper->getProperty(key, mLocalBuffer);
45 } 45 }
46 qWarning() << "no mapping available for key " << key;
46 return QVariant(); 47 return QVariant();
47 } 48 }
48 49
@@ -67,14 +68,23 @@ DummyEventAdaptorFactory::DummyEventAdaptorFactory()
67{ 68{
68 mResourceMapper = QSharedPointer<PropertyMapper<DummyEvent> >::create(); 69 mResourceMapper = QSharedPointer<PropertyMapper<DummyEvent> >::create();
69 mResourceMapper->mReadAccessors.insert("summary", [](DummyEvent const *buffer) -> QVariant { 70 mResourceMapper->mReadAccessors.insert("summary", [](DummyEvent const *buffer) -> QVariant {
70 return QString::fromStdString(buffer->summary()->c_str()); 71 if (buffer->summary()) {
72 return QString::fromStdString(buffer->summary()->c_str());
73 }
74 return QVariant();
71 }); 75 });
72 mLocalMapper = QSharedPointer<PropertyMapper<Akonadi2::Domain::Buffer::Event> >::create(); 76 mLocalMapper = QSharedPointer<PropertyMapper<Akonadi2::Domain::Buffer::Event> >::create();
73 mLocalMapper->mReadAccessors.insert("summary", [](Akonadi2::Domain::Buffer::Event const *buffer) -> QVariant { 77 mLocalMapper->mReadAccessors.insert("summary", [](Akonadi2::Domain::Buffer::Event const *buffer) -> QVariant {
74 return QString::fromStdString(buffer->summary()->c_str()); 78 if (buffer->summary()) {
79 return QString::fromStdString(buffer->summary()->c_str());
80 }
81 return QVariant();
75 }); 82 });
76 mLocalMapper->mReadAccessors.insert("uid", [](Akonadi2::Domain::Buffer::Event const *buffer) -> QVariant { 83 mLocalMapper->mReadAccessors.insert("uid", [](Akonadi2::Domain::Buffer::Event const *buffer) -> QVariant {
77 return QString::fromStdString(buffer->uid()->c_str()); 84 if (buffer->uid()) {
85 return QString::fromStdString(buffer->uid()->c_str());
86 }
87 return QVariant();
78 }); 88 });
79 89
80} 90}
@@ -94,7 +104,7 @@ QSharedPointer<Akonadi2::Domain::BufferAdaptor> DummyEventAdaptorFactory::create
94 if (auto metadataData = entity.metadata()) { 104 if (auto metadataData = entity.metadata()) {
95 flatbuffers::Verifier verifyer(metadataData->Data(), metadataData->size()); 105 flatbuffers::Verifier verifyer(metadataData->Data(), metadataData->size());
96 if (Akonadi2::VerifyMetadataBuffer(verifyer)) { 106 if (Akonadi2::VerifyMetadataBuffer(verifyer)) {
97 metadataBuffer = Akonadi2::GetMetadata(metadataData); 107 metadataBuffer = Akonadi2::GetMetadata(metadataData->Data());
98 } 108 }
99 } 109 }
100 110
@@ -102,15 +112,15 @@ QSharedPointer<Akonadi2::Domain::BufferAdaptor> DummyEventAdaptorFactory::create
102 if (auto localData = entity.local()) { 112 if (auto localData = entity.local()) {
103 flatbuffers::Verifier verifyer(localData->Data(), localData->size()); 113 flatbuffers::Verifier verifyer(localData->Data(), localData->size());
104 if (Akonadi2::Domain::Buffer::VerifyEventBuffer(verifyer)) { 114 if (Akonadi2::Domain::Buffer::VerifyEventBuffer(verifyer)) {
105 localBuffer = Akonadi2::Domain::Buffer::GetEvent(localData); 115 localBuffer = Akonadi2::Domain::Buffer::GetEvent(localData->Data());
106 } 116 }
107 } 117 }
108 118
109 auto adaptor = QSharedPointer<DummyEventAdaptor>::create(); 119 auto adaptor = QSharedPointer<DummyEventAdaptor>::create();
110 adaptor->mLocalBuffer = localBuffer; 120 adaptor->mLocalBuffer = localBuffer;
121 adaptor->mLocalMapper = mLocalMapper;
111 adaptor->mResourceBuffer = resourceBuffer; 122 adaptor->mResourceBuffer = resourceBuffer;
112 adaptor->mResourceMapper = mResourceMapper; 123 adaptor->mResourceMapper = mResourceMapper;
113 adaptor->mLocalMapper = mLocalMapper;
114 return adaptor; 124 return adaptor;
115} 125}
116 126
@@ -135,6 +145,6 @@ void DummyEventAdaptorFactory::createBuffer(const Akonadi2::Domain::Event &event
135 Akonadi2::Domain::Buffer::FinishEventBuffer(localFbb, location); 145 Akonadi2::Domain::Buffer::FinishEventBuffer(localFbb, location);
136 } 146 }
137 147
138 Akonadi2::EntityBuffer::assembleEntityBuffer(fbb, localFbb.GetBufferPointer(), localFbb.GetSize(), eventFbb.GetBufferPointer(), eventFbb.GetSize(), 0, 0); 148 Akonadi2::EntityBuffer::assembleEntityBuffer(fbb, 0, 0, eventFbb.GetBufferPointer(), eventFbb.GetSize(), localFbb.GetBufferPointer(), localFbb.GetSize());
139} 149}
140 150
diff --git a/dummyresource/facade.cpp b/dummyresource/facade.cpp
index cd930f6..1aaec68 100644
--- a/dummyresource/facade.cpp
+++ b/dummyresource/facade.cpp
@@ -31,6 +31,7 @@
31#include "createentity_generated.h" 31#include "createentity_generated.h"
32#include "domainadaptor.h" 32#include "domainadaptor.h"
33#include <common/entitybuffer.h> 33#include <common/entitybuffer.h>
34#include <common/index.h>
34 35
35using namespace DummyCalendar; 36using namespace DummyCalendar;
36using namespace flatbuffers; 37using namespace flatbuffers;
@@ -73,11 +74,11 @@ Async::Job<void> DummyResourceFacade::remove(const Akonadi2::Domain::Event &doma
73 return Async::null<void>(); 74 return Async::null<void>();
74} 75}
75 76
76static std::function<bool(const std::string &key, DummyEvent const *buffer)> prepareQuery(const Akonadi2::Query &query) 77static std::function<bool(const std::string &key, DummyEvent const *buffer, Akonadi2::Domain::Buffer::Event const *local)> prepareQuery(const Akonadi2::Query &query)
77{ 78{
78 //Compose some functions to make query matching fast. 79 //Compose some functions to make query matching fast.
79 //This way we can process the query once, and convert all values into something that can be compared quickly 80 //This way we can process the query once, and convert all values into something that can be compared quickly
80 std::function<bool(const std::string &key, DummyEvent const *buffer)> preparedQuery; 81 std::function<bool(const std::string &key, DummyEvent const *buffer, Akonadi2::Domain::Buffer::Event const *local)> preparedQuery;
81 if (!query.ids.isEmpty()) { 82 if (!query.ids.isEmpty()) {
82 //Match by id 83 //Match by id
83 //TODO: for id's a direct lookup would be way faster 84 //TODO: for id's a direct lookup would be way faster
@@ -88,15 +89,26 @@ static std::function<bool(const std::string &key, DummyEvent const *buffer)> pre
88 for (const auto &id : query.ids) { 89 for (const auto &id : query.ids) {
89 ids << id.toStdString(); 90 ids << id.toStdString();
90 } 91 }
91 preparedQuery = [ids](const std::string &key, DummyEvent const *buffer) { 92 preparedQuery = [ids](const std::string &key, DummyEvent const *buffer, Akonadi2::Domain::Buffer::Event const *local) {
92 if (ids.contains(key)) { 93 if (ids.contains(key)) {
93 return true; 94 return true;
94 } 95 }
95 return false; 96 return false;
96 }; 97 };
98 } else if (!query.propertyFilter.isEmpty()) {
99 if (query.propertyFilter.contains("uid")) {
100 const QByteArray uid = query.propertyFilter.value("uid").toByteArray();
101 preparedQuery = [uid](const std::string &key, DummyEvent const *buffer, Akonadi2::Domain::Buffer::Event const *local) {
102 if (local && local->uid() && (QByteArray::fromRawData(local->uid()->c_str(), local->uid()->size()) == uid)) {
103 qDebug() << "uid match";
104 return true;
105 }
106 return false;
107 };
108 }
97 } else { 109 } else {
98 //Match everything 110 //Match everything
99 preparedQuery = [](const std::string &key, DummyEvent const *buffer) { 111 preparedQuery = [](const std::string &key, DummyEvent const *buffer, Akonadi2::Domain::Buffer::Event const *local) {
100 return true; 112 return true;
101 }; 113 };
102 } 114 }
@@ -121,65 +133,96 @@ Async::Job<void> DummyResourceFacade::synchronizeResource(bool sync)
121 return Async::null<void>(); 133 return Async::null<void>();
122} 134}
123 135
124Async::Job<void> DummyResourceFacade::load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback) 136void DummyResourceFacade::readValue(QSharedPointer<Akonadi2::Storage> storage, const QByteArray &key, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback, std::function<bool(const std::string &key, DummyEvent const *buffer, Akonadi2::Domain::Buffer::Event const *local)> preparedQuery)
125{ 137{
126 qDebug() << "load called"; 138 storage->scan(key.data(), key.size(), [=](void *keyValue, int keySize, void *dataValue, int dataSize) -> bool {
127 139
128 return synchronizeResource(query.syncOnDemand).then<void>([=](Async::Future<void> &future) { 140 //Skip internals
129 qDebug() << "sync complete"; 141 if (QByteArray::fromRawData(static_cast<char*>(keyValue), keySize).startsWith("__internal")) {
130 //Now that the sync is complete we can execute the query 142 return true;
131 const auto preparedQuery = prepareQuery(query); 143 }
132
133 auto storage = QSharedPointer<Akonadi2::Storage>::create(Akonadi2::Store::storageLocation(), "org.kde.dummy");
134 144
135 qDebug() << "executing query"; 145 //Extract buffers
136 //We start a transaction explicitly that we'll leave open so the values can be read. 146 Akonadi2::EntityBuffer buffer(dataValue, dataSize);
137 //The transaction will be closed automatically once the storage object is destroyed.
138 storage->startTransaction(Akonadi2::Storage::ReadOnly);
139 //Because we have no indexes yet, we always do a full scan
140 storage->scan("", [=](void *keyValue, int keySize, void *dataValue, int dataSize) -> bool {
141 147
142 //Skip internals 148 DummyEvent const *resourceBuffer = 0;
143 if (QByteArray::fromRawData(static_cast<char*>(keyValue), keySize).startsWith("__internal")) { 149 if (auto resourceData = buffer.entity().resource()) {
144 return true; 150 flatbuffers::Verifier verifyer(resourceData->Data(), resourceData->size());
151 if (VerifyDummyEventBuffer(verifyer)) {
152 resourceBuffer = GetDummyEvent(resourceData->Data());
145 } 153 }
154 }
146 155
147 //Extract buffers 156 Akonadi2::Domain::Buffer::Event const *localBuffer = 0;
148 Akonadi2::EntityBuffer buffer(dataValue, dataSize); 157 if (auto localData = buffer.entity().local()) {
149 158 flatbuffers::Verifier verifyer(localData->Data(), localData->size());
150 DummyEvent const *resourceBuffer = 0; 159 if (Akonadi2::Domain::Buffer::VerifyEventBuffer(verifyer)) {
151 if (auto resourceData = buffer.entity().resource()) { 160 localBuffer = Akonadi2::Domain::Buffer::GetEvent(localData->Data());
152 flatbuffers::Verifier verifyer(resourceData->Data(), resourceData->size());
153 if (VerifyDummyEventBuffer(verifyer)) {
154 resourceBuffer = GetDummyEvent(resourceData);
155 }
156 } 161 }
162 }
157 163
158 Akonadi2::Metadata const *metadataBuffer = 0; 164 Akonadi2::Metadata const *metadataBuffer = 0;
159 if (auto metadataData = buffer.entity().metadata()) { 165 if (auto metadataData = buffer.entity().metadata()) {
160 flatbuffers::Verifier verifyer(metadataData->Data(), metadataData->size()); 166 flatbuffers::Verifier verifyer(metadataData->Data(), metadataData->size());
161 if (Akonadi2::VerifyMetadataBuffer(verifyer)) { 167 if (Akonadi2::VerifyMetadataBuffer(verifyer)) {
162 metadataBuffer = Akonadi2::GetMetadata(metadataData); 168 metadataBuffer = Akonadi2::GetMetadata(metadataData->Data());
163 }
164 } 169 }
170 }
165 171
166 if (!resourceBuffer || !metadataBuffer) { 172 if (!resourceBuffer || !metadataBuffer) {
167 qWarning() << "invalid buffer " << QString::fromStdString(std::string(static_cast<char*>(keyValue), keySize)); 173 qWarning() << "invalid buffer " << QString::fromStdString(std::string(static_cast<char*>(keyValue), keySize));
168 return true; 174 return true;
169 } 175 }
176
177 //We probably only want to create all buffers after the scan
178 //TODO use adapter for query and scan?
179 if (preparedQuery && preparedQuery(std::string(static_cast<char*>(keyValue), keySize), resourceBuffer, localBuffer)) {
180 qint64 revision = metadataBuffer ? metadataBuffer->revision() : -1;
181 //This only works for a 1:1 mapping of resource to domain types.
182 //Not i.e. for tags that are stored as flags in each entity of an imap store.
183 auto adaptor = mFactory->createAdaptor(buffer.entity());
184 //TODO only copy requested properties
185 auto memoryAdaptor = QSharedPointer<Akonadi2::Domain::MemoryBufferAdaptor>::create(*adaptor);
186 auto event = QSharedPointer<Akonadi2::Domain::Event>::create("org.kde.dummy", QString::fromUtf8(static_cast<char*>(keyValue), keySize), revision, memoryAdaptor);
187 resultCallback(event);
188 }
189 return true;
190 },
191 [](const Akonadi2::Storage::Error &error) {
192 qDebug() << QString::fromStdString(error.message);
193 });
194}
195
196Async::Job<void> DummyResourceFacade::load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback)
197{
198 return synchronizeResource(query.syncOnDemand).then<void>([=](Async::Future<void> &future) {
199 //Now that the sync is complete we can execute the query
200 const auto preparedQuery = prepareQuery(query);
201
202 auto storage = QSharedPointer<Akonadi2::Storage>::create(Akonadi2::Store::storageLocation(), "org.kde.dummy");
170 203
171 //We probably only want to create all buffers after the scan 204 QVector<QByteArray> keys;
172 //TODO use adapter for query and scan? 205 if (query.propertyFilter.contains("uid")) {
173 if (preparedQuery && preparedQuery(std::string(static_cast<char*>(keyValue), keySize), resourceBuffer)) { 206 static Index uidIndex(Akonadi2::Store::storageLocation(), "org.kde.dummy.index.uid", Akonadi2::Storage::ReadOnly);
174 qint64 revision = metadataBuffer ? metadataBuffer->revision() : -1; 207 uidIndex.lookup(query.propertyFilter.value("uid").toByteArray(), [&](const QByteArray &value) {
175 auto adaptor = mFactory->createAdaptor(buffer.entity()); 208 keys << value;
176 //TODO only copy requested properties 209 },
177 auto memoryAdaptor = QSharedPointer<Akonadi2::Domain::MemoryBufferAdaptor>::create(*adaptor); 210 [](const Index::Error &error) {
178 auto event = QSharedPointer<Akonadi2::Domain::Event>::create("org.kde.dummy", QString::fromUtf8(static_cast<char*>(keyValue), keySize), revision, memoryAdaptor); 211 qWarning() << "Error in index: " << QString::fromStdString(error.message);
179 resultCallback(event); 212 });
213 }
214
215 //We start a transaction explicitly that we'll leave open so the values can be read.
216 //The transaction will be closed automatically once the storage object is destroyed.
217 storage->startTransaction(Akonadi2::Storage::ReadOnly);
218 if (keys.isEmpty()) {
219 qDebug() << "full scan";
220 readValue(storage, QByteArray(), resultCallback, preparedQuery);
221 } else {
222 for (const auto &key : keys) {
223 readValue(storage, key, resultCallback, preparedQuery);
180 } 224 }
181 return true; 225 }
182 });
183 future.setFinished(); 226 future.setFinished();
184 }); 227 });
185} 228}
diff --git a/dummyresource/facade.h b/dummyresource/facade.h
index da0b1d6..c9c8047 100644
--- a/dummyresource/facade.h
+++ b/dummyresource/facade.h
@@ -43,6 +43,7 @@ public:
43 virtual Async::Job<void> load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback); 43 virtual Async::Job<void> load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback);
44 44
45private: 45private:
46 void readValue(QSharedPointer<Akonadi2::Storage> storage, const QByteArray &key, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback, std::function<bool(const std::string &key, DummyCalendar::DummyEvent const *buffer, Akonadi2::Domain::Buffer::Event const *local)>);
46 Async::Job<void> synchronizeResource(bool sync); 47 Async::Job<void> synchronizeResource(bool sync);
47 QSharedPointer<Akonadi2::ResourceAccess> mResourceAccess; 48 QSharedPointer<Akonadi2::ResourceAccess> mResourceAccess;
48 QSharedPointer<DomainTypeAdaptorFactory<Akonadi2::Domain::Event, Akonadi2::Domain::Buffer::Event, DummyCalendar::DummyEvent> > mFactory; 49 QSharedPointer<DomainTypeAdaptorFactory<Akonadi2::Domain::Event, Akonadi2::Domain::Buffer::Event, DummyCalendar::DummyEvent> > mFactory;
diff --git a/dummyresource/resourcefactory.cpp b/dummyresource/resourcefactory.cpp
index 60a9cf6..18083cb 100644
--- a/dummyresource/resourcefactory.cpp
+++ b/dummyresource/resourcefactory.cpp
@@ -28,6 +28,7 @@
28#include "domainadaptor.h" 28#include "domainadaptor.h"
29#include "commands.h" 29#include "commands.h"
30#include "clientapi.h" 30#include "clientapi.h"
31#include "index.h"
31#include <QUuid> 32#include <QUuid>
32#include <assert.h> 33#include <assert.h>
33 34
@@ -261,8 +262,31 @@ void DummyResource::configurePipeline(Akonadi2::Pipeline *pipeline)
261 auto adaptor = eventFactory->createAdaptor(entity); 262 auto adaptor = eventFactory->createAdaptor(entity);
262 qDebug() << "Summary preprocessor: " << adaptor->getProperty("summary").toString(); 263 qDebug() << "Summary preprocessor: " << adaptor->getProperty("summary").toString();
263 }); 264 });
265
266 auto uidIndexer = new SimpleProcessor("uidIndexer", [eventFactory](const Akonadi2::PipelineState &state, const Akonadi2::Entity &entity) {
267 static Index uidIndex(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/akonadi2/storage", "org.kde.dummy.index.uid", Akonadi2::Storage::ReadWrite);
268
269 auto adaptor = eventFactory->createAdaptor(entity);
270 const auto uid = adaptor->getProperty("uid");
271 if (uid.isValid()) {
272 uidIndex.add(uid.toByteArray(), state.key());
273 }
274
275 //TODO would this be worthwhile for performance reasons?
276 // flatbuffers::Verifier verifyer(entity.local()->Data(), entity.local()->size());
277 // if (!Akonadi2::Domain::Buffer::VerifyEventBuffer(verifyer)) {
278 // qWarning() << "invalid local buffer";
279 // return;
280 // }
281 // auto localEvent = Akonadi2::Domain::Buffer::GetEvent(entity.local()->Data());
282 // if (localEvent && localEvent->uid()) {
283 // qDebug() << "got uid: " << QByteArray::fromRawData(reinterpret_cast<const char *>(localEvent->uid()->Data()), localEvent->uid()->size());
284 // uidIndex.add(QByteArray::fromRawData(reinterpret_cast<const char *>(localEvent->uid()->Data()), localEvent->uid()->size()), state.key());
285 // }
286 });
287
264 //event is the entitytype and not the domain type 288 //event is the entitytype and not the domain type
265 pipeline->setPreprocessors("event", Akonadi2::Pipeline::NewPipeline, QVector<Akonadi2::Preprocessor*>() << eventIndexer); 289 pipeline->setPreprocessors("event", Akonadi2::Pipeline::NewPipeline, QVector<Akonadi2::Preprocessor*>() << eventIndexer << uidIndexer);
266 mProcessor = new Processor(pipeline, QList<MessageQueue*>() << &mUserQueue << &mSynchronizerQueue); 290 mProcessor = new Processor(pipeline, QList<MessageQueue*>() << &mUserQueue << &mSynchronizerQueue);
267 QObject::connect(mProcessor, &Processor::error, [this](int errorCode, const QString &msg) { onProcessorError(errorCode, msg); }); 291 QObject::connect(mProcessor, &Processor::error, [this](int errorCode, const QString &msg) { onProcessorError(errorCode, msg); });
268} 292}
diff --git a/tests/dummyresourcetest.cpp b/tests/dummyresourcetest.cpp
index 56e5513..4e9d0a5 100644
--- a/tests/dummyresourcetest.cpp
+++ b/tests/dummyresourcetest.cpp
@@ -29,6 +29,7 @@ private Q_SLOTS:
29 removeFromDisk("org.kde.dummy"); 29 removeFromDisk("org.kde.dummy");
30 removeFromDisk("org.kde.dummy.userqueue"); 30 removeFromDisk("org.kde.dummy.userqueue");
31 removeFromDisk("org.kde.dummy.synchronizerqueue"); 31 removeFromDisk("org.kde.dummy.synchronizerqueue");
32 removeFromDisk("org.kde.dummy.index.uid");
32 } 33 }
33 34
34 void cleanup() 35 void cleanup()
@@ -36,6 +37,7 @@ private Q_SLOTS:
36 removeFromDisk("org.kde.dummy"); 37 removeFromDisk("org.kde.dummy");
37 removeFromDisk("org.kde.dummy.userqueue"); 38 removeFromDisk("org.kde.dummy.userqueue");
38 removeFromDisk("org.kde.dummy.synchronizerqueue"); 39 removeFromDisk("org.kde.dummy.synchronizerqueue");
40 removeFromDisk("org.kde.dummy.index.uid");
39 } 41 }
40 42
41 void testProcessCommand() 43 void testProcessCommand()
@@ -50,8 +52,17 @@ private Q_SLOTS:
50 Akonadi2::Domain::Buffer::FinishEventBuffer(eventFbb, eventLocation); 52 Akonadi2::Domain::Buffer::FinishEventBuffer(eventFbb, eventLocation);
51 } 53 }
52 54
55 flatbuffers::FlatBufferBuilder localFbb;
56 {
57 auto uid = localFbb.CreateString("testuid");
58 auto localBuilder = Akonadi2::Domain::Buffer::EventBuilder(localFbb);
59 localBuilder.add_uid(uid);
60 auto location = localBuilder.Finish();
61 Akonadi2::Domain::Buffer::FinishEventBuffer(localFbb, location);
62 }
63
53 flatbuffers::FlatBufferBuilder entityFbb; 64 flatbuffers::FlatBufferBuilder entityFbb;
54 Akonadi2::EntityBuffer::assembleEntityBuffer(entityFbb, 0, 0, eventFbb.GetBufferPointer(), eventFbb.GetSize(), 0, 0); 65 Akonadi2::EntityBuffer::assembleEntityBuffer(entityFbb, 0, 0, eventFbb.GetBufferPointer(), eventFbb.GetSize(), localFbb.GetBufferPointer(), localFbb.GetSize());
55 66
56 flatbuffers::FlatBufferBuilder fbb; 67 flatbuffers::FlatBufferBuilder fbb;
57 auto type = fbb.CreateString(Akonadi2::Domain::getTypeName<Akonadi2::Domain::Event>().toStdString().data()); 68 auto type = fbb.CreateString(Akonadi2::Domain::getTypeName<Akonadi2::Domain::Event>().toStdString().data());
@@ -82,15 +93,34 @@ private Q_SLOTS:
82 QCOMPARE(revisionSpy.count(), 2); 93 QCOMPARE(revisionSpy.count(), 2);
83 } 94 }
84 95
85 void testWriteToFacade() 96 void testProperty()
97 {
98 Akonadi2::Domain::Event event;
99 event.setProperty("uid", "testuid");
100 QCOMPARE(event.getProperty("uid").toByteArray(), QByteArray("testuid"));
101 }
102
103 void testWriteToFacadeAndQueryByUid()
86 { 104 {
87 Akonadi2::Query query;
88 Akonadi2::Domain::Event event; 105 Akonadi2::Domain::Event event;
106 event.setProperty("uid", "testuid");
107 QCOMPARE(event.getProperty("uid").toByteArray(), QByteArray("testuid"));
89 event.setProperty("summary", "summaryValue"); 108 event.setProperty("summary", "summaryValue");
90 Akonadi2::Store::create<Akonadi2::Domain::Event>(event, "org.kde.dummy"); 109 Akonadi2::Store::create<Akonadi2::Domain::Event>(event, "org.kde.dummy");
91 110
92 QTest::qWait(1000); 111 //TODO required to ensure all messages have been processed. The query should ensure this.
93 //TODO wait for success response 112 QTest::qWait(300);
113
114 Akonadi2::Query query;
115 query.resources << "org.kde.dummy";
116 query.syncOnDemand = false;
117
118 query.propertyFilter.insert("uid", "testuid");
119 async::SyncListResult<Akonadi2::Domain::Event::Ptr> result(Akonadi2::Store::load<Akonadi2::Domain::Event>(query));
120 result.exec();
121 QCOMPARE(result.size(), 1);
122 auto value = result.first();
123 QCOMPARE(value->getProperty("uid").toByteArray(), QByteArray("testuid"));
94 } 124 }
95 125
96 void testResourceSync() 126 void testResourceSync()