summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2015-12-13 19:21:52 +0100
committerChristian Mollekopf <chrigi_1@fastmail.fm>2015-12-13 19:21:52 +0100
commit1aa82ab9cfacca1ee9af9f9137caeede55f89230 (patch)
treede1b6e2193fdc4213178da748beb5c388d01af0a
parent1999bbb377561e6e83c126de536a26df18870970 (diff)
downloadsink-1aa82ab9cfacca1ee9af9f9137caeede55f89230.tar.gz
sink-1aa82ab9cfacca1ee9af9f9137caeede55f89230.zip
Load entities from multiple resources
-rw-r--r--common/clientapi.cpp14
-rw-r--r--common/modelresult.cpp27
-rw-r--r--common/resultprovider.h55
-rw-r--r--tests/clientapitest.cpp46
4 files changed, 117 insertions, 25 deletions
diff --git a/common/clientapi.cpp b/common/clientapi.cpp
index 644d60c..46f7534 100644
--- a/common/clientapi.cpp
+++ b/common/clientapi.cpp
@@ -98,22 +98,24 @@ QSharedPointer<QAbstractItemModel> Store::loadModel(Query query)
98 resultProvider->initialResultSetComplete(typename DomainType::Ptr()); 98 resultProvider->initialResultSetComplete(typename DomainType::Ptr());
99 return model; 99 return model;
100 } 100 }
101 auto aggregatingEmitter = AggregatingResultEmitter<typename DomainType::Ptr>::Ptr::create();
102 model->setEmitter(aggregatingEmitter);
101 KAsync::iterate(resources) 103 KAsync::iterate(resources)
102 .template each<void, QByteArray>([query, model](const QByteArray &resource, KAsync::Future<void> &future) { 104 .template each<void, QByteArray>([query, aggregatingEmitter](const QByteArray &resource, KAsync::Future<void> &future) {
103 auto facade = FacadeFactory::instance().getFacade<DomainType>(resourceName(resource), resource); 105 auto facade = FacadeFactory::instance().getFacade<DomainType>(resourceName(resource), resource);
104 if (facade) { 106 if (facade) {
105 Trace() << "Trying to fetch from resource"; 107 Trace() << "Trying to fetch from resource " << resource;
106 auto result = facade->load(query); 108 auto result = facade->load(query);
107 auto emitter = result.second; 109 aggregatingEmitter->addEmitter(result.second);
108 //TODO use aggregating emitter instead
109 model->setEmitter(emitter);
110 model->fetchMore(QModelIndex());
111 result.first.template then<void>([&future](){future.setFinished();}).exec(); 110 result.first.template then<void>([&future](){future.setFinished();}).exec();
112 } else { 111 } else {
112 Trace() << "Couldn' find a facade for " << resource;
113 //Ignore the error and carry on 113 //Ignore the error and carry on
114 future.setFinished(); 114 future.setFinished();
115 } 115 }
116 }).exec(); 116 }).exec();
117 //TODO if the aggregatingEmitter is still empty we're done
118 model->fetchMore(QModelIndex());
117 119
118 return model; 120 return model;
119} 121}
diff --git a/common/modelresult.cpp b/common/modelresult.cpp
index d18dba1..690a17e 100644
--- a/common/modelresult.cpp
+++ b/common/modelresult.cpp
@@ -24,6 +24,13 @@
24#include "domain/folder.h" 24#include "domain/folder.h"
25#include "log.h" 25#include "log.h"
26 26
27static uint qHash(const Akonadi2::ApplicationDomain::ApplicationDomainType &type)
28{
29 Q_ASSERT(!type.resourceInstanceIdentifier().isEmpty());
30 Q_ASSERT(!type.identifier().isEmpty());
31 return qHash(type.resourceInstanceIdentifier() + type.identifier());
32}
33
27template<class T, class Ptr> 34template<class T, class Ptr>
28ModelResult<T, Ptr>::ModelResult(const Akonadi2::Query &query, const QList<QByteArray> &propertyColumns) 35ModelResult<T, Ptr>::ModelResult(const Akonadi2::Query &query, const QList<QByteArray> &propertyColumns)
29 :QAbstractItemModel(), 36 :QAbstractItemModel(),
@@ -44,9 +51,9 @@ template<class T, class Ptr>
44qint64 ModelResult<T, Ptr>::parentId(const Ptr &value) 51qint64 ModelResult<T, Ptr>::parentId(const Ptr &value)
45{ 52{
46 if (!mQuery.parentProperty.isEmpty()) { 53 if (!mQuery.parentProperty.isEmpty()) {
47 const auto property = value->getProperty(mQuery.parentProperty).toByteArray(); 54 const auto identifier = value->getProperty(mQuery.parentProperty).toByteArray();
48 if (!property.isEmpty()) { 55 if (!identifier.isEmpty()) {
49 return qHash(property); 56 return qHash(T(value->resourceInstanceIdentifier(), identifier, 0, QSharedPointer<Akonadi2::ApplicationDomain::BufferAdaptor>()));
50 } 57 }
51 } 58 }
52 return 0; 59 return 0;
@@ -152,7 +159,7 @@ void ModelResult<T, Ptr>::fetchMore(const QModelIndex &parent)
152template<class T, class Ptr> 159template<class T, class Ptr>
153void ModelResult<T, Ptr>::add(const Ptr &value) 160void ModelResult<T, Ptr>::add(const Ptr &value)
154{ 161{
155 const auto childId = qHash(value->identifier()); 162 const auto childId = qHash(*value);
156 const auto id = parentId(value); 163 const auto id = parentId(value);
157 //Ignore updates we get before the initial fetch is done 164 //Ignore updates we get before the initial fetch is done
158 if (!mEntityChildrenFetched.contains(id)) { 165 if (!mEntityChildrenFetched.contains(id)) {
@@ -184,11 +191,11 @@ void ModelResult<T, Ptr>::add(const Ptr &value)
184template<class T, class Ptr> 191template<class T, class Ptr>
185void ModelResult<T, Ptr>::remove(const Ptr &value) 192void ModelResult<T, Ptr>::remove(const Ptr &value)
186{ 193{
187 auto childId = qHash(value->identifier()); 194 auto childId = qHash(*value);
188 auto id = parentId(value); 195 auto id = parentId(value);
189 auto parent = createIndexFromId(id); 196 auto parent = createIndexFromId(id);
190 // qDebug() << "Removed entity" << childId; 197 // qDebug() << "Removed entity" << childId;
191 auto index = mTree[id].indexOf(qHash(value->identifier())); 198 auto index = mTree[id].indexOf(childId);
192 beginRemoveRows(parent, index, index); 199 beginRemoveRows(parent, index, index);
193 mEntities.remove(childId); 200 mEntities.remove(childId);
194 mTree[id].removeAll(childId); 201 mTree[id].removeAll(childId);
@@ -202,7 +209,7 @@ void ModelResult<T, Ptr>::fetchEntities(const QModelIndex &parent)
202{ 209{
203 const auto id = getIdentifier(parent); 210 const auto id = getIdentifier(parent);
204 mEntityChildrenFetched.insert(id); 211 mEntityChildrenFetched.insert(id);
205 Trace() << "Loading child entities"; 212 Trace() << "Loading child entities of parent " << id;
206 if (loadEntities) { 213 if (loadEntities) {
207 loadEntities(parent.data(DomainObjectRole).template value<Ptr>()); 214 loadEntities(parent.data(DomainObjectRole).template value<Ptr>());
208 } else { 215 } else {
@@ -220,7 +227,7 @@ void ModelResult<T, Ptr>::setFetcher(const std::function<void(const Ptr &parent)
220template<class T, class Ptr> 227template<class T, class Ptr>
221void ModelResult<T, Ptr>::setEmitter(const typename Akonadi2::ResultEmitter<Ptr>::Ptr &emitter) 228void ModelResult<T, Ptr>::setEmitter(const typename Akonadi2::ResultEmitter<Ptr>::Ptr &emitter)
222{ 229{
223 setFetcher(emitter->mFetcher); 230 setFetcher([this](const Ptr &parent) {mEmitter->fetch(parent);});
224 emitter->onAdded([this](const Ptr &value) { 231 emitter->onAdded([this](const Ptr &value) {
225 this->add(value); 232 this->add(value);
226 }); 233 });
@@ -231,7 +238,7 @@ void ModelResult<T, Ptr>::setEmitter(const typename Akonadi2::ResultEmitter<Ptr>
231 this->remove(value); 238 this->remove(value);
232 }); 239 });
233 emitter->onInitialResultSetComplete([this](const Ptr &parent) { 240 emitter->onInitialResultSetComplete([this](const Ptr &parent) {
234 const qint64 parentId = parent ? qHash(parent->identifier()) : 0; 241 const qint64 parentId = parent ? qHash(*parent) : 0;
235 const auto parentIndex = createIndexFromId(parentId); 242 const auto parentIndex = createIndexFromId(parentId);
236 mEntityChildrenFetchComplete.insert(parentId); 243 mEntityChildrenFetchComplete.insert(parentId);
237 emit dataChanged(parentIndex, parentIndex, QVector<int>() << ChildrenFetchedRole); 244 emit dataChanged(parentIndex, parentIndex, QVector<int>() << ChildrenFetchedRole);
@@ -248,7 +255,7 @@ bool ModelResult<T, Ptr>::childrenFetched(const QModelIndex &index) const
248template<class T, class Ptr> 255template<class T, class Ptr>
249void ModelResult<T, Ptr>::modify(const Ptr &value) 256void ModelResult<T, Ptr>::modify(const Ptr &value)
250{ 257{
251 auto childId = qHash(value->identifier()); 258 auto childId = qHash(*value);
252 auto id = parentId(value); 259 auto id = parentId(value);
253 //Ignore updates we get before the initial fetch is done 260 //Ignore updates we get before the initial fetch is done
254 if (!mEntityChildrenFetched.contains(id)) { 261 if (!mEntityChildrenFetched.contains(id)) {
diff --git a/common/resultprovider.h b/common/resultprovider.h
index d50f3f6..d94ad44 100644
--- a/common/resultprovider.h
+++ b/common/resultprovider.h
@@ -235,6 +235,11 @@ class ResultEmitter {
235public: 235public:
236 typedef QSharedPointer<ResultEmitter<DomainType> > Ptr; 236 typedef QSharedPointer<ResultEmitter<DomainType> > Ptr;
237 237
238 virtual ~ResultEmitter()
239 {
240
241 }
242
238 void onAdded(const std::function<void(const DomainType&)> &handler) 243 void onAdded(const std::function<void(const DomainType&)> &handler)
239 { 244 {
240 addHandler = handler; 245 addHandler = handler;
@@ -306,7 +311,12 @@ public:
306 mFetcher = fetcher; 311 mFetcher = fetcher;
307 } 312 }
308 313
309 std::function<void(const DomainType &parent)> mFetcher; 314 virtual void fetch(const DomainType &parent)
315 {
316 if (mFetcher) {
317 mFetcher(parent);
318 }
319 }
310 320
311private: 321private:
312 friend class ResultProvider<DomainType>; 322 friend class ResultProvider<DomainType>;
@@ -317,8 +327,51 @@ private:
317 std::function<void(const DomainType&)> initialResultSetCompleteHandler; 327 std::function<void(const DomainType&)> initialResultSetCompleteHandler;
318 std::function<void(void)> completeHandler; 328 std::function<void(void)> completeHandler;
319 std::function<void(void)> clearHandler; 329 std::function<void(void)> clearHandler;
330
331 std::function<void(const DomainType &parent)> mFetcher;
320 ThreadBoundary mThreadBoundary; 332 ThreadBoundary mThreadBoundary;
321}; 333};
322 334
335template<class DomainType>
336class AggregatingResultEmitter : public ResultEmitter<DomainType> {
337public:
338 typedef QSharedPointer<AggregatingResultEmitter<DomainType> > Ptr;
339
340 void addEmitter(const typename ResultEmitter<DomainType>::Ptr &emitter)
341 {
342 emitter->onAdded([this](const DomainType &value) {
343 this->add(value);
344 });
345 emitter->onModified([this](const DomainType &value) {
346 this->modify(value);
347 });
348 emitter->onRemoved([this](const DomainType &value) {
349 this->remove(value);
350 });
351 emitter->onInitialResultSetComplete([this](const DomainType &value) {
352 this->initialResultSetComplete(value);
353 });
354 emitter->onComplete([this]() {
355 this->complete();
356 });
357 emitter->onClear([this]() {
358 this->clear();
359 });
360 mEmitter << emitter;
361 }
362
363 void fetch(const DomainType &parent) Q_DECL_OVERRIDE
364 {
365 for (const auto &emitter : mEmitter) {
366 emitter->fetch(parent);
367 }
368 }
369
370private:
371 QList<typename ResultEmitter<DomainType>::Ptr> mEmitter;
372};
373
374
375
323} 376}
324 377
diff --git a/tests/clientapitest.cpp b/tests/clientapitest.cpp
index 8f956ab..5777e68 100644
--- a/tests/clientapitest.cpp
+++ b/tests/clientapitest.cpp
@@ -13,12 +13,18 @@ template <typename T>
13class DummyResourceFacade : public Akonadi2::StoreFacade<T> 13class DummyResourceFacade : public Akonadi2::StoreFacade<T>
14{ 14{
15public: 15public:
16 static std::shared_ptr<DummyResourceFacade<T> > registerFacade() 16 static std::shared_ptr<DummyResourceFacade<T> > registerFacade(const QByteArray &instanceIdentifier = QByteArray())
17 { 17 {
18 static QMap<QByteArray, std::shared_ptr<DummyResourceFacade<T> > > map;
18 auto facade = std::make_shared<DummyResourceFacade<T> >(); 19 auto facade = std::make_shared<DummyResourceFacade<T> >();
20 map.insert(instanceIdentifier, facade);
21 bool alwaysReturnFacade = instanceIdentifier.isEmpty();
19 Akonadi2::FacadeFactory::instance().registerFacade<T, DummyResourceFacade<T> >("dummyresource", 22 Akonadi2::FacadeFactory::instance().registerFacade<T, DummyResourceFacade<T> >("dummyresource",
20 [facade](const QByteArray &instanceIdentifier) { 23 [alwaysReturnFacade](const QByteArray &instanceIdentifier) {
21 return facade; 24 if (alwaysReturnFacade) {
25 return map.value(QByteArray());
26 }
27 return map.value(instanceIdentifier);
22 } 28 }
23 ); 29 );
24 return facade; 30 return facade;
@@ -38,10 +44,17 @@ public:
38 auto emitter = resultProvider->emitter(); 44 auto emitter = resultProvider->emitter();
39 45
40 resultProvider->setFetcher([query, resultProvider, this](const typename T::Ptr &parent) { 46 resultProvider->setFetcher([query, resultProvider, this](const typename T::Ptr &parent) {
41 Trace() << "Running the fetcher"; 47 if (parent) {
48 Trace() << "Running the fetcher " << parent->identifier();
49 } else {
50 Trace() << "Running the fetcher.";
51 }
52 Trace() << "-------------------------.";
42 for (const auto &res : results) { 53 for (const auto &res : results) {
43 qDebug() << "Parent filter " << query.propertyFilter.value("parent").toByteArray() << res->identifier(); 54 qDebug() << "Parent filter " << query.propertyFilter.value("parent").toByteArray() << res->identifier() << res->getProperty("parent").toByteArray();
44 if (!query.propertyFilter.contains("parent") || query.propertyFilter.value("parent").toByteArray() == res->getProperty("parent").toByteArray()) { 55 auto parentProperty = res->getProperty("parent").toByteArray();
56 if ((!parent && parentProperty.isEmpty()) || (parent && parentProperty == parent->identifier()) || query.parentProperty.isEmpty()) {
57 qDebug() << "Found a hit" << res->identifier();
45 resultProvider->add(res); 58 resultProvider->add(res);
46 } 59 }
47 } 60 }
@@ -180,8 +193,8 @@ private Q_SLOTS:
180 void testModelNestedLive() 193 void testModelNestedLive()
181 { 194 {
182 auto facade = DummyResourceFacade<Akonadi2::ApplicationDomain::Folder>::registerFacade(); 195 auto facade = DummyResourceFacade<Akonadi2::ApplicationDomain::Folder>::registerFacade();
183 auto folder = QSharedPointer<Akonadi2::ApplicationDomain::Folder>::create("resource", "id", 0, QSharedPointer<Akonadi2::ApplicationDomain::MemoryBufferAdaptor>::create()); 196 auto folder = QSharedPointer<Akonadi2::ApplicationDomain::Folder>::create("dummyresource.instance1", "id", 0, QSharedPointer<Akonadi2::ApplicationDomain::MemoryBufferAdaptor>::create());
184 auto subfolder = QSharedPointer<Akonadi2::ApplicationDomain::Folder>::create("resource", "subId", 0, QSharedPointer<Akonadi2::ApplicationDomain::MemoryBufferAdaptor>::create()); 197 auto subfolder = QSharedPointer<Akonadi2::ApplicationDomain::Folder>::create("dummyresource.instance1", "subId", 0, QSharedPointer<Akonadi2::ApplicationDomain::MemoryBufferAdaptor>::create());
185 subfolder->setProperty("parent", "id"); 198 subfolder->setProperty("parent", "id");
186 facade->results << folder << subfolder; 199 facade->results << folder << subfolder;
187 ResourceConfig::addResource("dummyresource.instance1", "dummyresource"); 200 ResourceConfig::addResource("dummyresource.instance1", "dummyresource");
@@ -230,6 +243,23 @@ private Q_SLOTS:
230 //TODO: A modification can also be a move 243 //TODO: A modification can also be a move
231 } 244 }
232 245
246 void testLoadMultiResource()
247 {
248 auto facade1 = DummyResourceFacade<Akonadi2::ApplicationDomain::Event>::registerFacade("dummyresource.instance1");
249 facade1->results << QSharedPointer<Akonadi2::ApplicationDomain::Event>::create("resource1", "id", 0, QSharedPointer<Akonadi2::ApplicationDomain::MemoryBufferAdaptor>::create());
250 auto facade2 = DummyResourceFacade<Akonadi2::ApplicationDomain::Event>::registerFacade("dummyresource.instance2");
251 facade2->results << QSharedPointer<Akonadi2::ApplicationDomain::Event>::create("resource2", "id", 0, QSharedPointer<Akonadi2::ApplicationDomain::MemoryBufferAdaptor>::create());
252 ResourceConfig::addResource("dummyresource.instance1", "dummyresource");
253 ResourceConfig::addResource("dummyresource.instance2", "dummyresource");
254
255 Akonadi2::Query query;
256 query.liveQuery = false;
257
258 auto model = Akonadi2::Store::loadModel<Akonadi2::ApplicationDomain::Event>(query);
259 QTRY_VERIFY(model->data(QModelIndex(), Akonadi2::Store::ChildrenFetchedRole).toBool());
260 QCOMPARE(model->rowCount(QModelIndex()), 2);
261 }
262
233 263
234}; 264};
235 265