summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/resourcefacade.cpp83
-rw-r--r--common/resourcefacade.h4
-rw-r--r--common/store.cpp68
-rw-r--r--tests/querytest.cpp19
4 files changed, 114 insertions, 60 deletions
diff --git a/common/resourcefacade.cpp b/common/resourcefacade.cpp
index 583d6ec..bf4239d 100644
--- a/common/resourcefacade.cpp
+++ b/common/resourcefacade.cpp
@@ -85,34 +85,35 @@ LocalStorageQueryRunner<DomainType>::LocalStorageQueryRunner(const Query &query,
85 updateStatus(*entity); 85 updateStatus(*entity);
86 mResultProvider->add(entity); 86 mResultProvider->add(entity);
87 } 87 }
88 if (query.liveQuery) {
89 {
90 auto ret = QObject::connect(&configNotifier, &ConfigNotifier::added, guard, [this](const ApplicationDomain::ApplicationDomainType::Ptr &entry) {
91 auto entity = entry.staticCast<DomainType>();
92 updateStatus(*entity);
93 mResultProvider->add(entity);
94 });
95 Q_ASSERT(ret);
96 }
97 {
98 auto ret = QObject::connect(&configNotifier, &ConfigNotifier::modified, guard, [this](const ApplicationDomain::ApplicationDomainType::Ptr &entry) {
99 auto entity = entry.staticCast<DomainType>();
100 updateStatus(*entity);
101 mResultProvider->modify(entity);
102 });
103 Q_ASSERT(ret);
104 }
105 {
106 auto ret = QObject::connect(&configNotifier, &ConfigNotifier::removed, guard, [this](const ApplicationDomain::ApplicationDomainType::Ptr &entry) {
107 mResultProvider->remove(entry.staticCast<DomainType>());
108 });
109 Q_ASSERT(ret);
110 }
111 }
112 // TODO initialResultSetComplete should be implicit 88 // TODO initialResultSetComplete should be implicit
113 mResultProvider->initialResultSetComplete(typename DomainType::Ptr()); 89 mResultProvider->initialResultSetComplete(typename DomainType::Ptr());
114 mResultProvider->complete(); 90 mResultProvider->complete();
115 }); 91 });
92 if (query.liveQuery) {
93 {
94 auto ret = QObject::connect(&configNotifier, &ConfigNotifier::added, guard, [this](const ApplicationDomain::ApplicationDomainType::Ptr &entry) {
95 auto entity = entry.staticCast<DomainType>();
96 SinkTrace() << "A new resource has been added: " << entity->identifier();
97 updateStatus(*entity);
98 mResultProvider->add(entity);
99 });
100 Q_ASSERT(ret);
101 }
102 {
103 auto ret = QObject::connect(&configNotifier, &ConfigNotifier::modified, guard, [this](const ApplicationDomain::ApplicationDomainType::Ptr &entry) {
104 auto entity = entry.staticCast<DomainType>();
105 updateStatus(*entity);
106 mResultProvider->modify(entity);
107 });
108 Q_ASSERT(ret);
109 }
110 {
111 auto ret = QObject::connect(&configNotifier, &ConfigNotifier::removed, guard, [this](const ApplicationDomain::ApplicationDomainType::Ptr &entry) {
112 mResultProvider->remove(entry.staticCast<DomainType>());
113 });
114 Q_ASSERT(ret);
115 }
116 }
116 mResultProvider->onDone([=]() { delete guard; delete this; }); 117 mResultProvider->onDone([=]() { delete guard; delete this; });
117} 118}
118 119
@@ -153,7 +154,7 @@ typename Sink::ResultEmitter<typename DomainType::Ptr>::Ptr LocalStorageQueryRun
153 154
154 155
155template <typename DomainType> 156template <typename DomainType>
156LocalStorageFacade<DomainType>::LocalStorageFacade(const QByteArray &identifier) : StoreFacade<DomainType>(), mIdentifier(identifier), mConfigStore(identifier) 157LocalStorageFacade<DomainType>::LocalStorageFacade(const QByteArray &identifier) : StoreFacade<DomainType>(), mIdentifier(identifier)
157{ 158{
158} 159}
159 160
@@ -163,19 +164,15 @@ LocalStorageFacade<DomainType>::~LocalStorageFacade()
163} 164}
164 165
165template <typename DomainType> 166template <typename DomainType>
166typename DomainType::Ptr LocalStorageFacade<DomainType>::readFromConfig(const QByteArray &id, const QByteArray &type)
167{
168 return ::readFromConfig<DomainType>(mConfigStore, id, type);
169}
170
171template <typename DomainType>
172KAsync::Job<void> LocalStorageFacade<DomainType>::create(const DomainType &domainObject) 167KAsync::Job<void> LocalStorageFacade<DomainType>::create(const DomainType &domainObject)
173{ 168{
174 return KAsync::start<void>([domainObject, this]() { 169 auto configStoreIdentifier = mIdentifier;
170 return KAsync::start<void>([domainObject, configStoreIdentifier]() {
175 const QByteArray type = domainObject.getProperty("type").toByteArray(); 171 const QByteArray type = domainObject.getProperty("type").toByteArray();
176 const QByteArray providedIdentifier = domainObject.identifier().isEmpty() ? domainObject.getProperty("identifier").toByteArray() : domainObject.identifier(); 172 const QByteArray providedIdentifier = domainObject.identifier().isEmpty() ? domainObject.getProperty("identifier").toByteArray() : domainObject.identifier();
177 const QByteArray identifier = providedIdentifier.isEmpty() ? ResourceConfig::newIdentifier(type) : providedIdentifier; 173 const QByteArray identifier = providedIdentifier.isEmpty() ? ResourceConfig::newIdentifier(type) : providedIdentifier;
178 mConfigStore.add(identifier, type); 174 auto configStore = ConfigStore(configStoreIdentifier);
175 configStore.add(identifier, type);
179 auto changedProperties = domainObject.changedProperties(); 176 auto changedProperties = domainObject.changedProperties();
180 changedProperties.removeOne("identifier"); 177 changedProperties.removeOne("identifier");
181 changedProperties.removeOne("type"); 178 changedProperties.removeOne("type");
@@ -185,16 +182,17 @@ KAsync::Job<void> LocalStorageFacade<DomainType>::create(const DomainType &domai
185 for (const auto &property : changedProperties) { 182 for (const auto &property : changedProperties) {
186 configurationValues.insert(property, domainObject.getProperty(property)); 183 configurationValues.insert(property, domainObject.getProperty(property));
187 } 184 }
188 mConfigStore.modify(identifier, configurationValues); 185 configStore.modify(identifier, configurationValues);
189 } 186 }
190 sConfigNotifier.add(readFromConfig(identifier, type)); 187 sConfigNotifier.add(::readFromConfig<DomainType>(configStore, identifier, type));
191 }); 188 });
192} 189}
193 190
194template <typename DomainType> 191template <typename DomainType>
195KAsync::Job<void> LocalStorageFacade<DomainType>::modify(const DomainType &domainObject) 192KAsync::Job<void> LocalStorageFacade<DomainType>::modify(const DomainType &domainObject)
196{ 193{
197 return KAsync::start<void>([domainObject, this]() { 194 auto configStoreIdentifier = mIdentifier;
195 return KAsync::start<void>([domainObject, configStoreIdentifier]() {
198 const QByteArray identifier = domainObject.identifier(); 196 const QByteArray identifier = domainObject.identifier();
199 if (identifier.isEmpty()) { 197 if (identifier.isEmpty()) {
200 SinkWarning() << "We need an \"identifier\" property to identify the entity to configure."; 198 SinkWarning() << "We need an \"identifier\" property to identify the entity to configure.";
@@ -203,31 +201,34 @@ KAsync::Job<void> LocalStorageFacade<DomainType>::modify(const DomainType &domai
203 auto changedProperties = domainObject.changedProperties(); 201 auto changedProperties = domainObject.changedProperties();
204 changedProperties.removeOne("identifier"); 202 changedProperties.removeOne("identifier");
205 changedProperties.removeOne("type"); 203 changedProperties.removeOne("type");
204 auto configStore = ConfigStore(configStoreIdentifier);
206 if (!changedProperties.isEmpty()) { 205 if (!changedProperties.isEmpty()) {
207 // We have some configuration values 206 // We have some configuration values
208 QMap<QByteArray, QVariant> configurationValues; 207 QMap<QByteArray, QVariant> configurationValues;
209 for (const auto &property : changedProperties) { 208 for (const auto &property : changedProperties) {
210 configurationValues.insert(property, domainObject.getProperty(property)); 209 configurationValues.insert(property, domainObject.getProperty(property));
211 } 210 }
212 mConfigStore.modify(identifier, configurationValues); 211 configStore.modify(identifier, configurationValues);
213 } 212 }
214 213
215 const auto type = mConfigStore.getEntries().value(identifier); 214 const auto type = configStore.getEntries().value(identifier);
216 sConfigNotifier.modify(readFromConfig(identifier, type)); 215 sConfigNotifier.modify(::readFromConfig<DomainType>(configStore, identifier, type));
217 }); 216 });
218} 217}
219 218
220template <typename DomainType> 219template <typename DomainType>
221KAsync::Job<void> LocalStorageFacade<DomainType>::remove(const DomainType &domainObject) 220KAsync::Job<void> LocalStorageFacade<DomainType>::remove(const DomainType &domainObject)
222{ 221{
223 return KAsync::start<void>([domainObject, this]() { 222 auto configStoreIdentifier = mIdentifier;
223 return KAsync::start<void>([domainObject, configStoreIdentifier]() {
224 const QByteArray identifier = domainObject.identifier(); 224 const QByteArray identifier = domainObject.identifier();
225 if (identifier.isEmpty()) { 225 if (identifier.isEmpty()) {
226 SinkWarning() << "We need an \"identifier\" property to identify the entity to configure"; 226 SinkWarning() << "We need an \"identifier\" property to identify the entity to configure";
227 return; 227 return;
228 } 228 }
229 SinkTrace() << "Removing: " << identifier; 229 SinkTrace() << "Removing: " << identifier;
230 mConfigStore.remove(identifier); 230 auto configStore = ConfigStore(configStoreIdentifier);
231 configStore.remove(identifier);
231 sConfigNotifier.remove(QSharedPointer<DomainType>::create(domainObject)); 232 sConfigNotifier.remove(QSharedPointer<DomainType>::create(domainObject));
232 }); 233 });
233} 234}
diff --git a/common/resourcefacade.h b/common/resourcefacade.h
index 23c453a..b87a396 100644
--- a/common/resourcefacade.h
+++ b/common/resourcefacade.h
@@ -87,10 +87,6 @@ public:
87protected: 87protected:
88 QByteArray mIdentifier; 88 QByteArray mIdentifier;
89 static ConfigNotifier sConfigNotifier; 89 static ConfigNotifier sConfigNotifier;
90
91private:
92 typename DomainType::Ptr readFromConfig(const QByteArray &id, const QByteArray &type);
93 ConfigStore mConfigStore;
94}; 90};
95 91
96class ResourceFacade : public LocalStorageFacade<Sink::ApplicationDomain::SinkResource> 92class ResourceFacade : public LocalStorageFacade<Sink::ApplicationDomain::SinkResource>
diff --git a/common/store.cpp b/common/store.cpp
index 848afae..363878c 100644
--- a/common/store.cpp
+++ b/common/store.cpp
@@ -38,6 +38,8 @@
38 38
39SINK_DEBUG_AREA("store") 39SINK_DEBUG_AREA("store")
40 40
41Q_DECLARE_METATYPE(QSharedPointer<Sink::ResultEmitter<Sink::ApplicationDomain::SinkResource::Ptr>>)
42
41namespace Sink { 43namespace Sink {
42 44
43QString Store::storageLocation() 45QString Store::storageLocation()
@@ -95,6 +97,27 @@ static QMap<QByteArray, QByteArray> getResources(const QList<QByteArray> &resour
95 return resources; 97 return resources;
96} 98}
97 99
100
101template <class DomainType>
102KAsync::Job<void> queryResource(const QByteArray resourceType, const QByteArray &resourceInstanceIdentifier, const Query &query, typename AggregatingResultEmitter<typename DomainType::Ptr>::Ptr aggregatingEmitter)
103{
104 auto facade = FacadeFactory::instance().getFacade<DomainType>(resourceType, resourceInstanceIdentifier);
105 if (facade) {
106 SinkTrace() << "Trying to fetch from resource " << resourceInstanceIdentifier;
107 auto result = facade->load(query);
108 if (result.second) {
109 aggregatingEmitter->addEmitter(result.second);
110 } else {
111 SinkWarning() << "Null emitter for resource " << resourceInstanceIdentifier;
112 }
113 return result.first;
114 } else {
115 SinkTrace() << "Couldn' find a facade for " << resourceInstanceIdentifier;
116 // Ignore the error and carry on
117 return KAsync::null<void>();
118 }
119}
120
98template <class DomainType> 121template <class DomainType>
99QSharedPointer<QAbstractItemModel> Store::loadModel(Query query) 122QSharedPointer<QAbstractItemModel> Store::loadModel(Query query)
100{ 123{
@@ -117,24 +140,41 @@ QSharedPointer<QAbstractItemModel> Store::loadModel(Query query)
117 auto resources = getResources(query.resources, query.accounts, ApplicationDomain::getTypeName<DomainType>()); 140 auto resources = getResources(query.resources, query.accounts, ApplicationDomain::getTypeName<DomainType>());
118 auto aggregatingEmitter = AggregatingResultEmitter<typename DomainType::Ptr>::Ptr::create(); 141 auto aggregatingEmitter = AggregatingResultEmitter<typename DomainType::Ptr>::Ptr::create();
119 model->setEmitter(aggregatingEmitter); 142 model->setEmitter(aggregatingEmitter);
143
144 if (query.liveQuery && query.resources.isEmpty() && !ApplicationDomain::isGlobalType(ApplicationDomain::getTypeName<DomainType>())) {
145 SinkTrace() << "Listening for new resources";
146 auto facade = FacadeFactory::instance().getFacade<ApplicationDomain::SinkResource>("", "");
147 Q_ASSERT(facade);
148 Sink::Query resourceQuery;
149 resourceQuery.liveQuery = query.liveQuery;
150 auto result = facade->load(resourceQuery);
151 auto emitter = result.second;
152 emitter->onAdded([query, aggregatingEmitter](const ApplicationDomain::SinkResource::Ptr &resource) {
153 SinkTrace() << "Found new resources: " << resource->identifier();
154 const auto resourceType = ResourceConfig::getResourceType(resource->identifier());
155 Q_ASSERT(!resourceType.isEmpty());
156 queryResource<DomainType>(resourceType, resource->identifier(), query, aggregatingEmitter).exec();
157 });
158 emitter->onModified([](const ApplicationDomain::SinkResource::Ptr &) {
159 });
160 emitter->onRemoved([](const ApplicationDomain::SinkResource::Ptr &) {
161 });
162 emitter->onInitialResultSetComplete([](const ApplicationDomain::SinkResource::Ptr &) {
163 });
164 emitter->onComplete([query, aggregatingEmitter]() {
165 SinkTrace() << "Resource query complete";
166
167 });
168 model->setProperty("resourceEmitter", QVariant::fromValue(emitter));
169 result.first.exec();
170 }
171
120 KAsync::iterate(resources.keys()) 172 KAsync::iterate(resources.keys())
121 .template each<void, QByteArray>([query, aggregatingEmitter, resources](const QByteArray &resourceInstanceIdentifier, KAsync::Future<void> &future) { 173 .template each<void, QByteArray>([query, aggregatingEmitter, resources](const QByteArray &resourceInstanceIdentifier, KAsync::Future<void> &future) {
122 const auto resourceType = resources.value(resourceInstanceIdentifier); 174 const auto resourceType = resources.value(resourceInstanceIdentifier);
123 auto facade = FacadeFactory::instance().getFacade<DomainType>(resourceType, resourceInstanceIdentifier); 175 queryResource<DomainType>(resourceType, resourceInstanceIdentifier, query, aggregatingEmitter).template then<void>([&future]() {
124 if (facade) {
125 SinkTrace() << "Trying to fetch from resource " << resourceInstanceIdentifier;
126 auto result = facade->load(query);
127 if (result.second) {
128 aggregatingEmitter->addEmitter(result.second);
129 } else {
130 SinkWarning() << "Null emitter for resource " << resourceInstanceIdentifier;
131 }
132 result.first.template then<void>([&future]() { future.setFinished(); }).exec();
133 } else {
134 SinkTrace() << "Couldn' find a facade for " << resourceInstanceIdentifier;
135 // Ignore the error and carry on
136 future.setFinished(); 176 future.setFinished();
137 } 177 }).exec();
138 }) 178 })
139 .exec(); 179 .exec();
140 model->fetchMore(QModelIndex()); 180 model->fetchMore(QModelIndex());
diff --git a/tests/querytest.cpp b/tests/querytest.cpp
index d3a97f6..d72dc7d 100644
--- a/tests/querytest.cpp
+++ b/tests/querytest.cpp
@@ -2,7 +2,7 @@
2 2
3#include <QString> 3#include <QString>
4 4
5#include "dummyresource/resourcefactory.h" 5#include "resource.h"
6#include "store.h" 6#include "store.h"
7#include "resourcecontrol.h" 7#include "resourcecontrol.h"
8#include "commands.h" 8#include "commands.h"
@@ -10,6 +10,7 @@
10#include "log.h" 10#include "log.h"
11#include "modelresult.h" 11#include "modelresult.h"
12#include "test.h" 12#include "test.h"
13#include "testutils.h"
13 14
14/** 15/**
15 * Test of the query system using the dummy resource. 16 * Test of the query system using the dummy resource.
@@ -323,6 +324,22 @@ private slots:
323 // We can't make any assumptions about the order of the indexes 324 // We can't make any assumptions about the order of the indexes
324 // QCOMPARE(model->index(1, 0).data(Sink::Store::DomainObjectRole).value<Sink::ApplicationDomain::Mail::Ptr>()->getProperty("uid").toByteArray(), QByteArray("testSecond")); 325 // QCOMPARE(model->index(1, 0).data(Sink::Store::DomainObjectRole).value<Sink::ApplicationDomain::Mail::Ptr>()->getProperty("uid").toByteArray(), QByteArray("testSecond"));
325 } 326 }
327
328 void testReactToNewResource()
329 {
330 Sink::Query query;
331 query.liveQuery = true;
332 auto model = Sink::Store::loadModel<Sink::ApplicationDomain::Folder>(query);
333 QTRY_COMPARE(model->rowCount(QModelIndex()), 0);
334
335 auto res = Sink::ApplicationDomain::DummyResource::create("");
336 VERIFYEXEC(Sink::Store::create(res));
337 auto folder = Sink::ApplicationDomain::Folder::create(res.identifier());
338 VERIFYEXEC(Sink::Store::create(folder));
339 QTRY_COMPARE(model->rowCount(QModelIndex()), 1);
340
341 VERIFYEXEC(Sink::Store::remove(res));
342 }
326}; 343};
327 344
328QTEST_MAIN(QueryTest) 345QTEST_MAIN(QueryTest)