diff options
-rw-r--r-- | common/resourcefacade.cpp | 83 | ||||
-rw-r--r-- | common/resourcefacade.h | 4 | ||||
-rw-r--r-- | common/store.cpp | 68 | ||||
-rw-r--r-- | tests/querytest.cpp | 19 |
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 | ||
155 | template <typename DomainType> | 156 | template <typename DomainType> |
156 | LocalStorageFacade<DomainType>::LocalStorageFacade(const QByteArray &identifier) : StoreFacade<DomainType>(), mIdentifier(identifier), mConfigStore(identifier) | 157 | LocalStorageFacade<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 | ||
165 | template <typename DomainType> | 166 | template <typename DomainType> |
166 | typename DomainType::Ptr LocalStorageFacade<DomainType>::readFromConfig(const QByteArray &id, const QByteArray &type) | ||
167 | { | ||
168 | return ::readFromConfig<DomainType>(mConfigStore, id, type); | ||
169 | } | ||
170 | |||
171 | template <typename DomainType> | ||
172 | KAsync::Job<void> LocalStorageFacade<DomainType>::create(const DomainType &domainObject) | 167 | KAsync::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 | ||
194 | template <typename DomainType> | 191 | template <typename DomainType> |
195 | KAsync::Job<void> LocalStorageFacade<DomainType>::modify(const DomainType &domainObject) | 192 | KAsync::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 | ||
220 | template <typename DomainType> | 219 | template <typename DomainType> |
221 | KAsync::Job<void> LocalStorageFacade<DomainType>::remove(const DomainType &domainObject) | 220 | KAsync::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: | |||
87 | protected: | 87 | protected: |
88 | QByteArray mIdentifier; | 88 | QByteArray mIdentifier; |
89 | static ConfigNotifier sConfigNotifier; | 89 | static ConfigNotifier sConfigNotifier; |
90 | |||
91 | private: | ||
92 | typename DomainType::Ptr readFromConfig(const QByteArray &id, const QByteArray &type); | ||
93 | ConfigStore mConfigStore; | ||
94 | }; | 90 | }; |
95 | 91 | ||
96 | class ResourceFacade : public LocalStorageFacade<Sink::ApplicationDomain::SinkResource> | 92 | class 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 | ||
39 | SINK_DEBUG_AREA("store") | 39 | SINK_DEBUG_AREA("store") |
40 | 40 | ||
41 | Q_DECLARE_METATYPE(QSharedPointer<Sink::ResultEmitter<Sink::ApplicationDomain::SinkResource::Ptr>>) | ||
42 | |||
41 | namespace Sink { | 43 | namespace Sink { |
42 | 44 | ||
43 | QString Store::storageLocation() | 45 | QString 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 | |||
101 | template <class DomainType> | ||
102 | KAsync::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 | |||
98 | template <class DomainType> | 121 | template <class DomainType> |
99 | QSharedPointer<QAbstractItemModel> Store::loadModel(Query query) | 122 | QSharedPointer<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 | ||
328 | QTEST_MAIN(QueryTest) | 345 | QTEST_MAIN(QueryTest) |