From b948ffa771054e8a7780ccfc38de11762bcdfcd6 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Tue, 12 Jul 2016 16:08:14 +0200 Subject: React to new resources --- common/resourcefacade.cpp | 83 ++++++++++++++++++++++++----------------------- common/resourcefacade.h | 4 --- common/store.cpp | 68 ++++++++++++++++++++++++++++++-------- 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::LocalStorageQueryRunner(const Query &query, updateStatus(*entity); mResultProvider->add(entity); } - if (query.liveQuery) { - { - auto ret = QObject::connect(&configNotifier, &ConfigNotifier::added, guard, [this](const ApplicationDomain::ApplicationDomainType::Ptr &entry) { - auto entity = entry.staticCast(); - updateStatus(*entity); - mResultProvider->add(entity); - }); - Q_ASSERT(ret); - } - { - auto ret = QObject::connect(&configNotifier, &ConfigNotifier::modified, guard, [this](const ApplicationDomain::ApplicationDomainType::Ptr &entry) { - auto entity = entry.staticCast(); - updateStatus(*entity); - mResultProvider->modify(entity); - }); - Q_ASSERT(ret); - } - { - auto ret = QObject::connect(&configNotifier, &ConfigNotifier::removed, guard, [this](const ApplicationDomain::ApplicationDomainType::Ptr &entry) { - mResultProvider->remove(entry.staticCast()); - }); - Q_ASSERT(ret); - } - } // TODO initialResultSetComplete should be implicit mResultProvider->initialResultSetComplete(typename DomainType::Ptr()); mResultProvider->complete(); }); + if (query.liveQuery) { + { + auto ret = QObject::connect(&configNotifier, &ConfigNotifier::added, guard, [this](const ApplicationDomain::ApplicationDomainType::Ptr &entry) { + auto entity = entry.staticCast(); + SinkTrace() << "A new resource has been added: " << entity->identifier(); + updateStatus(*entity); + mResultProvider->add(entity); + }); + Q_ASSERT(ret); + } + { + auto ret = QObject::connect(&configNotifier, &ConfigNotifier::modified, guard, [this](const ApplicationDomain::ApplicationDomainType::Ptr &entry) { + auto entity = entry.staticCast(); + updateStatus(*entity); + mResultProvider->modify(entity); + }); + Q_ASSERT(ret); + } + { + auto ret = QObject::connect(&configNotifier, &ConfigNotifier::removed, guard, [this](const ApplicationDomain::ApplicationDomainType::Ptr &entry) { + mResultProvider->remove(entry.staticCast()); + }); + Q_ASSERT(ret); + } + } mResultProvider->onDone([=]() { delete guard; delete this; }); } @@ -153,7 +154,7 @@ typename Sink::ResultEmitter::Ptr LocalStorageQueryRun template -LocalStorageFacade::LocalStorageFacade(const QByteArray &identifier) : StoreFacade(), mIdentifier(identifier), mConfigStore(identifier) +LocalStorageFacade::LocalStorageFacade(const QByteArray &identifier) : StoreFacade(), mIdentifier(identifier) { } @@ -162,20 +163,16 @@ LocalStorageFacade::~LocalStorageFacade() { } -template -typename DomainType::Ptr LocalStorageFacade::readFromConfig(const QByteArray &id, const QByteArray &type) -{ - return ::readFromConfig(mConfigStore, id, type); -} - template KAsync::Job LocalStorageFacade::create(const DomainType &domainObject) { - return KAsync::start([domainObject, this]() { + auto configStoreIdentifier = mIdentifier; + return KAsync::start([domainObject, configStoreIdentifier]() { const QByteArray type = domainObject.getProperty("type").toByteArray(); const QByteArray providedIdentifier = domainObject.identifier().isEmpty() ? domainObject.getProperty("identifier").toByteArray() : domainObject.identifier(); const QByteArray identifier = providedIdentifier.isEmpty() ? ResourceConfig::newIdentifier(type) : providedIdentifier; - mConfigStore.add(identifier, type); + auto configStore = ConfigStore(configStoreIdentifier); + configStore.add(identifier, type); auto changedProperties = domainObject.changedProperties(); changedProperties.removeOne("identifier"); changedProperties.removeOne("type"); @@ -185,16 +182,17 @@ KAsync::Job LocalStorageFacade::create(const DomainType &domai for (const auto &property : changedProperties) { configurationValues.insert(property, domainObject.getProperty(property)); } - mConfigStore.modify(identifier, configurationValues); + configStore.modify(identifier, configurationValues); } - sConfigNotifier.add(readFromConfig(identifier, type)); + sConfigNotifier.add(::readFromConfig(configStore, identifier, type)); }); } template KAsync::Job LocalStorageFacade::modify(const DomainType &domainObject) { - return KAsync::start([domainObject, this]() { + auto configStoreIdentifier = mIdentifier; + return KAsync::start([domainObject, configStoreIdentifier]() { const QByteArray identifier = domainObject.identifier(); if (identifier.isEmpty()) { SinkWarning() << "We need an \"identifier\" property to identify the entity to configure."; @@ -203,31 +201,34 @@ KAsync::Job LocalStorageFacade::modify(const DomainType &domai auto changedProperties = domainObject.changedProperties(); changedProperties.removeOne("identifier"); changedProperties.removeOne("type"); + auto configStore = ConfigStore(configStoreIdentifier); if (!changedProperties.isEmpty()) { // We have some configuration values QMap configurationValues; for (const auto &property : changedProperties) { configurationValues.insert(property, domainObject.getProperty(property)); } - mConfigStore.modify(identifier, configurationValues); + configStore.modify(identifier, configurationValues); } - const auto type = mConfigStore.getEntries().value(identifier); - sConfigNotifier.modify(readFromConfig(identifier, type)); + const auto type = configStore.getEntries().value(identifier); + sConfigNotifier.modify(::readFromConfig(configStore, identifier, type)); }); } template KAsync::Job LocalStorageFacade::remove(const DomainType &domainObject) { - return KAsync::start([domainObject, this]() { + auto configStoreIdentifier = mIdentifier; + return KAsync::start([domainObject, configStoreIdentifier]() { const QByteArray identifier = domainObject.identifier(); if (identifier.isEmpty()) { SinkWarning() << "We need an \"identifier\" property to identify the entity to configure"; return; } SinkTrace() << "Removing: " << identifier; - mConfigStore.remove(identifier); + auto configStore = ConfigStore(configStoreIdentifier); + configStore.remove(identifier); sConfigNotifier.remove(QSharedPointer::create(domainObject)); }); } 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: protected: QByteArray mIdentifier; static ConfigNotifier sConfigNotifier; - -private: - typename DomainType::Ptr readFromConfig(const QByteArray &id, const QByteArray &type); - ConfigStore mConfigStore; }; class ResourceFacade : public LocalStorageFacade 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 @@ SINK_DEBUG_AREA("store") +Q_DECLARE_METATYPE(QSharedPointer>) + namespace Sink { QString Store::storageLocation() @@ -95,6 +97,27 @@ static QMap getResources(const QList &resour return resources; } + +template +KAsync::Job queryResource(const QByteArray resourceType, const QByteArray &resourceInstanceIdentifier, const Query &query, typename AggregatingResultEmitter::Ptr aggregatingEmitter) +{ + auto facade = FacadeFactory::instance().getFacade(resourceType, resourceInstanceIdentifier); + if (facade) { + SinkTrace() << "Trying to fetch from resource " << resourceInstanceIdentifier; + auto result = facade->load(query); + if (result.second) { + aggregatingEmitter->addEmitter(result.second); + } else { + SinkWarning() << "Null emitter for resource " << resourceInstanceIdentifier; + } + return result.first; + } else { + SinkTrace() << "Couldn' find a facade for " << resourceInstanceIdentifier; + // Ignore the error and carry on + return KAsync::null(); + } +} + template QSharedPointer Store::loadModel(Query query) { @@ -117,24 +140,41 @@ QSharedPointer Store::loadModel(Query query) auto resources = getResources(query.resources, query.accounts, ApplicationDomain::getTypeName()); auto aggregatingEmitter = AggregatingResultEmitter::Ptr::create(); model->setEmitter(aggregatingEmitter); + + if (query.liveQuery && query.resources.isEmpty() && !ApplicationDomain::isGlobalType(ApplicationDomain::getTypeName())) { + SinkTrace() << "Listening for new resources"; + auto facade = FacadeFactory::instance().getFacade("", ""); + Q_ASSERT(facade); + Sink::Query resourceQuery; + resourceQuery.liveQuery = query.liveQuery; + auto result = facade->load(resourceQuery); + auto emitter = result.second; + emitter->onAdded([query, aggregatingEmitter](const ApplicationDomain::SinkResource::Ptr &resource) { + SinkTrace() << "Found new resources: " << resource->identifier(); + const auto resourceType = ResourceConfig::getResourceType(resource->identifier()); + Q_ASSERT(!resourceType.isEmpty()); + queryResource(resourceType, resource->identifier(), query, aggregatingEmitter).exec(); + }); + emitter->onModified([](const ApplicationDomain::SinkResource::Ptr &) { + }); + emitter->onRemoved([](const ApplicationDomain::SinkResource::Ptr &) { + }); + emitter->onInitialResultSetComplete([](const ApplicationDomain::SinkResource::Ptr &) { + }); + emitter->onComplete([query, aggregatingEmitter]() { + SinkTrace() << "Resource query complete"; + + }); + model->setProperty("resourceEmitter", QVariant::fromValue(emitter)); + result.first.exec(); + } + KAsync::iterate(resources.keys()) .template each([query, aggregatingEmitter, resources](const QByteArray &resourceInstanceIdentifier, KAsync::Future &future) { const auto resourceType = resources.value(resourceInstanceIdentifier); - auto facade = FacadeFactory::instance().getFacade(resourceType, resourceInstanceIdentifier); - if (facade) { - SinkTrace() << "Trying to fetch from resource " << resourceInstanceIdentifier; - auto result = facade->load(query); - if (result.second) { - aggregatingEmitter->addEmitter(result.second); - } else { - SinkWarning() << "Null emitter for resource " << resourceInstanceIdentifier; - } - result.first.template then([&future]() { future.setFinished(); }).exec(); - } else { - SinkTrace() << "Couldn' find a facade for " << resourceInstanceIdentifier; - // Ignore the error and carry on + queryResource(resourceType, resourceInstanceIdentifier, query, aggregatingEmitter).template then([&future]() { future.setFinished(); - } + }).exec(); }) .exec(); 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 @@ #include -#include "dummyresource/resourcefactory.h" +#include "resource.h" #include "store.h" #include "resourcecontrol.h" #include "commands.h" @@ -10,6 +10,7 @@ #include "log.h" #include "modelresult.h" #include "test.h" +#include "testutils.h" /** * Test of the query system using the dummy resource. @@ -323,6 +324,22 @@ private slots: // We can't make any assumptions about the order of the indexes // QCOMPARE(model->index(1, 0).data(Sink::Store::DomainObjectRole).value()->getProperty("uid").toByteArray(), QByteArray("testSecond")); } + + void testReactToNewResource() + { + Sink::Query query; + query.liveQuery = true; + auto model = Sink::Store::loadModel(query); + QTRY_COMPARE(model->rowCount(QModelIndex()), 0); + + auto res = Sink::ApplicationDomain::DummyResource::create(""); + VERIFYEXEC(Sink::Store::create(res)); + auto folder = Sink::ApplicationDomain::Folder::create(res.identifier()); + VERIFYEXEC(Sink::Store::create(folder)); + QTRY_COMPARE(model->rowCount(QModelIndex()), 1); + + VERIFYEXEC(Sink::Store::remove(res)); + } }; QTEST_MAIN(QueryTest) -- cgit v1.2.3