diff options
-rw-r--r-- | common/CMakeLists.txt | 1 | ||||
-rw-r--r-- | common/clientapi.h | 2 | ||||
-rw-r--r-- | common/commands/modifyentity.fbs | 4 | ||||
-rw-r--r-- | common/entitybuffer.cpp | 1 | ||||
-rw-r--r-- | common/resourceaccess.cpp | 2 | ||||
-rw-r--r-- | common/storage_lmdb.cpp | 5 | ||||
-rw-r--r-- | dummyresource/domainadaptor.cpp | 26 | ||||
-rw-r--r-- | dummyresource/facade.cpp | 145 | ||||
-rw-r--r-- | dummyresource/facade.h | 1 | ||||
-rw-r--r-- | dummyresource/resourcefactory.cpp | 26 | ||||
-rw-r--r-- | tests/dummyresourcetest.cpp | 40 |
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 | ||
37 | add_library(${PROJECT_NAME} SHARED ${command_SRCS}) | 38 | add_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 | ||
233 | private: | 233 | private: |
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; | |||
3 | table ModifyEntity { | 3 | table 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 | ||
11 | root_type ModifyEntity; | 11 | root_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 | ||
59 | void EntityBuffer::assembleEntityBuffer(flatbuffers::FlatBufferBuilder &fbb, void const *metadataData, size_t metadataSize, void const *resourceData, size_t resourceSize, void const *localData, size_t localSize) | 59 | void 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 | ||
35 | using namespace DummyCalendar; | 36 | using namespace DummyCalendar; |
36 | using namespace flatbuffers; | 37 | using 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 | ||
76 | static std::function<bool(const std::string &key, DummyEvent const *buffer)> prepareQuery(const Akonadi2::Query &query) | 77 | static 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 | ||
124 | Async::Job<void> DummyResourceFacade::load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback) | 136 | void 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 | |||
196 | Async::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 | ||
45 | private: | 45 | private: |
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() |