diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2015-12-13 19:21:52 +0100 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2015-12-13 19:21:52 +0100 |
commit | 1aa82ab9cfacca1ee9af9f9137caeede55f89230 (patch) | |
tree | de1b6e2193fdc4213178da748beb5c388d01af0a | |
parent | 1999bbb377561e6e83c126de536a26df18870970 (diff) | |
download | sink-1aa82ab9cfacca1ee9af9f9137caeede55f89230.tar.gz sink-1aa82ab9cfacca1ee9af9f9137caeede55f89230.zip |
Load entities from multiple resources
-rw-r--r-- | common/clientapi.cpp | 14 | ||||
-rw-r--r-- | common/modelresult.cpp | 27 | ||||
-rw-r--r-- | common/resultprovider.h | 55 | ||||
-rw-r--r-- | tests/clientapitest.cpp | 46 |
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 | ||
27 | static 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 | |||
27 | template<class T, class Ptr> | 34 | template<class T, class Ptr> |
28 | ModelResult<T, Ptr>::ModelResult(const Akonadi2::Query &query, const QList<QByteArray> &propertyColumns) | 35 | ModelResult<T, Ptr>::ModelResult(const Akonadi2::Query &query, const QList<QByteArray> &propertyColumns) |
29 | :QAbstractItemModel(), | 36 | :QAbstractItemModel(), |
@@ -44,9 +51,9 @@ template<class T, class Ptr> | |||
44 | qint64 ModelResult<T, Ptr>::parentId(const Ptr &value) | 51 | qint64 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) | |||
152 | template<class T, class Ptr> | 159 | template<class T, class Ptr> |
153 | void ModelResult<T, Ptr>::add(const Ptr &value) | 160 | void 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) | |||
184 | template<class T, class Ptr> | 191 | template<class T, class Ptr> |
185 | void ModelResult<T, Ptr>::remove(const Ptr &value) | 192 | void 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) | |||
220 | template<class T, class Ptr> | 227 | template<class T, class Ptr> |
221 | void ModelResult<T, Ptr>::setEmitter(const typename Akonadi2::ResultEmitter<Ptr>::Ptr &emitter) | 228 | void 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 | |||
248 | template<class T, class Ptr> | 255 | template<class T, class Ptr> |
249 | void ModelResult<T, Ptr>::modify(const Ptr &value) | 256 | void 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 { | |||
235 | public: | 235 | public: |
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 | ||
311 | private: | 321 | private: |
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 | ||
335 | template<class DomainType> | ||
336 | class AggregatingResultEmitter : public ResultEmitter<DomainType> { | ||
337 | public: | ||
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 | |||
370 | private: | ||
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> | |||
13 | class DummyResourceFacade : public Akonadi2::StoreFacade<T> | 13 | class DummyResourceFacade : public Akonadi2::StoreFacade<T> |
14 | { | 14 | { |
15 | public: | 15 | public: |
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 | ||