summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/resourcefacade.cpp242
-rw-r--r--common/resourcefacade.h71
-rw-r--r--tests/accountstest.cpp34
3 files changed, 207 insertions, 140 deletions
diff --git a/common/resourcefacade.cpp b/common/resourcefacade.cpp
index 2969c44..935c613 100644
--- a/common/resourcefacade.cpp
+++ b/common/resourcefacade.cpp
@@ -24,6 +24,136 @@
24#include "storage.h" 24#include "storage.h"
25#include <QDir> 25#include <QDir>
26 26
27template <typename DomainType>
28ConfigNotifier LocalStorageFacade<DomainType>::sConfigNotifier;
29
30template <typename DomainType>
31LocalStorageFacade<DomainType>::LocalStorageFacade(const QByteArray &identifier) : Sink::StoreFacade<DomainType>(), mConfigStore(identifier)
32{
33}
34
35template <typename DomainType>
36LocalStorageFacade<DomainType>::~LocalStorageFacade()
37{
38}
39
40template <typename DomainType>
41typename DomainType::Ptr LocalStorageFacade<DomainType>::readFromConfig(const QByteArray &id, const QByteArray &type)
42{
43 auto object = DomainType::Ptr::create(id);
44 object->setProperty("type", type);
45 const auto configurationValues = mConfigStore.get(id);
46 for (auto it = configurationValues.constBegin(); it != configurationValues.constEnd(); it++) {
47 object->setProperty(it.key(), it.value());
48 }
49 return object;
50}
51
52template <typename DomainType>
53KAsync::Job<void> LocalStorageFacade<DomainType>::create(const DomainType &domainObject)
54{
55 return KAsync::start<void>([domainObject, this]() {
56 const QByteArray type = domainObject.getProperty("type").toByteArray();
57 //FIXME use .identifier() instead
58 const QByteArray providedIdentifier = domainObject.getProperty("identifier").toByteArray();
59 const QByteArray identifier = providedIdentifier.isEmpty() ? ResourceConfig::newIdentifier(type) : providedIdentifier;
60 mConfigStore.add(identifier, type);
61 auto changedProperties = domainObject.changedProperties();
62 changedProperties.removeOne("identifier");
63 changedProperties.removeOne("type");
64 if (!changedProperties.isEmpty()) {
65 // We have some configuration values
66 QMap<QByteArray, QVariant> configurationValues;
67 for (const auto &property : changedProperties) {
68 configurationValues.insert(property, domainObject.getProperty(property));
69 }
70 mConfigStore.modify(identifier, configurationValues);
71 }
72 sConfigNotifier.add(readFromConfig(identifier, type));
73 });
74}
75
76template <typename DomainType>
77KAsync::Job<void> LocalStorageFacade<DomainType>::modify(const DomainType &domainObject)
78{
79 return KAsync::start<void>([domainObject, this]() {
80 const QByteArray identifier = domainObject.identifier();
81 if (identifier.isEmpty()) {
82 Warning() << "We need an \"identifier\" property to identify the entity to configure.";
83 return;
84 }
85 auto changedProperties = domainObject.changedProperties();
86 changedProperties.removeOne("identifier");
87 changedProperties.removeOne("type");
88 if (!changedProperties.isEmpty()) {
89 // We have some configuration values
90 QMap<QByteArray, QVariant> configurationValues;
91 for (const auto &property : changedProperties) {
92 configurationValues.insert(property, domainObject.getProperty(property));
93 }
94 mConfigStore.modify(identifier, configurationValues);
95 }
96
97 const auto type = mConfigStore.getEntries().value(identifier);
98 sConfigNotifier.modify(readFromConfig(identifier, type));
99 });
100}
101
102template <typename DomainType>
103KAsync::Job<void> LocalStorageFacade<DomainType>::remove(const DomainType &domainObject)
104{
105 return KAsync::start<void>([domainObject, this]() {
106 const QByteArray identifier = domainObject.identifier();
107 if (identifier.isEmpty()) {
108 Warning() << "We need an \"identifier\" property to identify the entity to configure";
109 return;
110 }
111 mConfigStore.remove(identifier);
112 sConfigNotifier.remove(QSharedPointer<DomainType>::create(domainObject));
113 });
114}
115
116template <typename DomainType>
117QPair<KAsync::Job<void>, typename Sink::ResultEmitter<typename DomainType::Ptr>::Ptr> LocalStorageFacade<DomainType>::load(const Sink::Query &query)
118{
119 QObject *guard = new QObject;
120 auto resultProvider = new Sink::ResultProvider<typename DomainType::Ptr>();
121 auto emitter = resultProvider->emitter();
122 resultProvider->setFetcher([](const QSharedPointer<DomainType> &) {});
123 resultProvider->onDone([=]() { delete resultProvider; delete guard; });
124 auto job = KAsync::start<void>([=]() {
125 const auto entries = mConfigStore.getEntries();
126 for (const auto &res : entries.keys()) {
127 const auto type = entries.value(res);
128 if (!query.ids.isEmpty() && !query.ids.contains(res)) {
129 continue;
130 }
131 resultProvider->add(readFromConfig(res, type));
132 }
133 if (query.liveQuery) {
134 QObject::connect(&sConfigNotifier, &ConfigNotifier::modified, guard, [resultProvider](const Sink::ApplicationDomain::ApplicationDomainType::Ptr &entry) {
135 resultProvider->modify(entry.staticCast<DomainType>());
136 });
137 QObject::connect(&sConfigNotifier, &ConfigNotifier::added, guard, [resultProvider](const Sink::ApplicationDomain::ApplicationDomainType::Ptr &entry) {
138 resultProvider->add(entry.staticCast<DomainType>());
139 });
140 QObject::connect(&sConfigNotifier, &ConfigNotifier::removed, guard,[resultProvider](const Sink::ApplicationDomain::ApplicationDomainType::Ptr &entry) {
141 resultProvider->remove(entry.staticCast<DomainType>());
142 });
143 }
144 // TODO initialResultSetComplete should be implicit
145 resultProvider->initialResultSetComplete(typename DomainType::Ptr());
146 resultProvider->complete();
147 });
148 return qMakePair(job, emitter);
149}
150
151
152
153
154
155
156
27ResourceFacade::ResourceFacade(const QByteArray &) : Sink::StoreFacade<Sink::ApplicationDomain::SinkResource>() 157ResourceFacade::ResourceFacade(const QByteArray &) : Sink::StoreFacade<Sink::ApplicationDomain::SinkResource>()
28{ 158{
29} 159}
@@ -145,22 +275,7 @@ QPair<KAsync::Job<void>, typename Sink::ResultEmitter<Sink::ApplicationDomain::S
145 275
146 276
147 277
148Q_GLOBAL_STATIC(ConfigNotifier, sConfig); 278AccountFacade::AccountFacade(const QByteArray &) : LocalStorageFacade<Sink::ApplicationDomain::SinkAccount>("accounts")
149
150static Sink::ApplicationDomain::SinkAccount::Ptr readAccountFromConfig(const QByteArray &id, const QByteArray &type)
151{
152 auto account = Sink::ApplicationDomain::SinkAccount::Ptr::create(id);
153
154 account->setProperty("type", type);
155 const auto configurationValues = ConfigStore("accounts").get(id);
156 for (auto it = configurationValues.constBegin(); it != configurationValues.constEnd(); it++) {
157 account->setProperty(it.key(), it.value());
158 }
159 return account;
160}
161
162
163AccountFacade::AccountFacade(const QByteArray &) : Sink::StoreFacade<Sink::ApplicationDomain::SinkAccount>(), mConfigStore("accounts")
164{ 279{
165} 280}
166 281
@@ -168,98 +283,3 @@ AccountFacade::~AccountFacade()
168{ 283{
169} 284}
170 285
171KAsync::Job<void> AccountFacade::create(const Sink::ApplicationDomain::SinkAccount &account)
172{
173 return KAsync::start<void>([account, this]() {
174 const QByteArray type = account.getProperty("type").toByteArray();
175 const QByteArray providedIdentifier = account.getProperty("identifier").toByteArray();
176 // It is currently a requirement that the account starts with the type
177 const QByteArray identifier = providedIdentifier.isEmpty() ? ResourceConfig::newIdentifier(type) : providedIdentifier;
178 mConfigStore.add(identifier, type);
179 auto changedProperties = account.changedProperties();
180 changedProperties.removeOne("identifier");
181 changedProperties.removeOne("type");
182 if (!changedProperties.isEmpty()) {
183 // We have some configuration values
184 QMap<QByteArray, QVariant> configurationValues;
185 for (const auto &property : changedProperties) {
186 configurationValues.insert(property, account.getProperty(property));
187 }
188 mConfigStore.modify(identifier, configurationValues);
189 }
190 sConfig->add(readAccountFromConfig(identifier, type));
191 });
192}
193
194KAsync::Job<void> AccountFacade::modify(const Sink::ApplicationDomain::SinkAccount &account)
195{
196 return KAsync::start<void>([account, this]() {
197 const QByteArray identifier = account.identifier();
198 if (identifier.isEmpty()) {
199 Warning() << "We need an \"identifier\" property to identify the account to configure.";
200 return;
201 }
202 auto changedProperties = account.changedProperties();
203 changedProperties.removeOne("identifier");
204 changedProperties.removeOne("type");
205 if (!changedProperties.isEmpty()) {
206 // We have some configuration values
207 QMap<QByteArray, QVariant> configurationValues;
208 for (const auto &property : changedProperties) {
209 configurationValues.insert(property, account.getProperty(property));
210 }
211 mConfigStore.modify(identifier, configurationValues);
212 }
213
214 const auto type = mConfigStore.getEntries().value(identifier);
215 sConfig->modify(readAccountFromConfig(identifier, type));
216 });
217}
218
219KAsync::Job<void> AccountFacade::remove(const Sink::ApplicationDomain::SinkAccount &account)
220{
221 return KAsync::start<void>([account, this]() {
222 const QByteArray identifier = account.identifier();
223 if (identifier.isEmpty()) {
224 Warning() << "We need an \"identifier\" property to identify the account to configure";
225 return;
226 }
227 mConfigStore.remove(identifier);
228 sConfig->remove(Sink::ApplicationDomain::SinkAccount::Ptr::create(account));
229 });
230}
231
232QPair<KAsync::Job<void>, typename Sink::ResultEmitter<Sink::ApplicationDomain::SinkAccount::Ptr>::Ptr> AccountFacade::load(const Sink::Query &query)
233{
234 QObject *guard = new QObject;
235 auto resultProvider = new Sink::ResultProvider<typename Sink::ApplicationDomain::SinkAccount::Ptr>();
236 auto emitter = resultProvider->emitter();
237 resultProvider->setFetcher([](const QSharedPointer<Sink::ApplicationDomain::SinkAccount> &) {});
238 resultProvider->onDone([=]() { delete resultProvider; delete guard; });
239 auto job = KAsync::start<void>([=]() {
240 const auto configuredAccounts = mConfigStore.getEntries();
241 for (const auto &res : configuredAccounts.keys()) {
242 const auto type = configuredAccounts.value(res);
243 if (!query.ids.isEmpty() && !query.ids.contains(res)) {
244 continue;
245 }
246
247 resultProvider->add(readAccountFromConfig(res, type));
248 }
249 if (query.liveQuery) {
250 QObject::connect(sConfig, &ConfigNotifier::modified, guard, [resultProvider](const Sink::ApplicationDomain::SinkAccount::Ptr &account) {
251 resultProvider->modify(account);
252 });
253 QObject::connect(sConfig, &ConfigNotifier::added, guard, [resultProvider](const Sink::ApplicationDomain::SinkAccount::Ptr &account) {
254 resultProvider->add(account);
255 });
256 QObject::connect(sConfig, &ConfigNotifier::removed, guard,[resultProvider](const Sink::ApplicationDomain::SinkAccount::Ptr &account) {
257 resultProvider->remove(account);
258 });
259 }
260 // TODO initialResultSetComplete should be implicit
261 resultProvider->initialResultSetComplete(Sink::ApplicationDomain::SinkAccount::Ptr());
262 resultProvider->complete();
263 });
264 return qMakePair(job, emitter);
265}
diff --git a/common/resourcefacade.h b/common/resourcefacade.h
index 0deb017..e7e39d3 100644
--- a/common/resourcefacade.h
+++ b/common/resourcefacade.h
@@ -31,6 +31,47 @@ class Query;
31class Inspection; 31class Inspection;
32} 32}
33 33
34class ConfigNotifier : public QObject
35{
36 Q_OBJECT
37public:
38 void add(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account)
39 {
40 emit added(account);
41 }
42
43 void remove(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account)
44 {
45 emit removed(account);
46 }
47
48 void modify(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account)
49 {
50 emit modified(account);
51 }
52signals:
53 void added(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account);
54 void removed(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account);
55 void modified(const Sink::ApplicationDomain::ApplicationDomainType::Ptr &account);
56};
57
58template <typename DomainType>
59class LocalStorageFacade : public Sink::StoreFacade<DomainType>
60{
61public:
62 LocalStorageFacade(const QByteArray &instanceIdentifier);
63 virtual ~LocalStorageFacade();
64 virtual KAsync::Job<void> create(const DomainType &resource) Q_DECL_OVERRIDE;
65 virtual KAsync::Job<void> modify(const DomainType &resource) Q_DECL_OVERRIDE;
66 virtual KAsync::Job<void> remove(const DomainType &resource) Q_DECL_OVERRIDE;
67 virtual QPair<KAsync::Job<void>, typename Sink::ResultEmitter<typename DomainType::Ptr>::Ptr> load(const Sink::Query &query) Q_DECL_OVERRIDE;
68private:
69 typename DomainType::Ptr readFromConfig(const QByteArray &id, const QByteArray &type);
70
71 ConfigStore mConfigStore;
72 static ConfigNotifier sConfigNotifier;
73};
74
34class ResourceFacade : public Sink::StoreFacade<Sink::ApplicationDomain::SinkResource> 75class ResourceFacade : public Sink::StoreFacade<Sink::ApplicationDomain::SinkResource>
35{ 76{
36public: 77public:
@@ -42,39 +83,11 @@ public:
42 QPair<KAsync::Job<void>, typename Sink::ResultEmitter<Sink::ApplicationDomain::SinkResource::Ptr>::Ptr> load(const Sink::Query &query) Q_DECL_OVERRIDE; 83 QPair<KAsync::Job<void>, typename Sink::ResultEmitter<Sink::ApplicationDomain::SinkResource::Ptr>::Ptr> load(const Sink::Query &query) Q_DECL_OVERRIDE;
43}; 84};
44 85
45class AccountFacade : public Sink::StoreFacade<Sink::ApplicationDomain::SinkAccount> 86class AccountFacade : public LocalStorageFacade<Sink::ApplicationDomain::SinkAccount>
46{ 87{
47public: 88public:
48 AccountFacade(const QByteArray &instanceIdentifier); 89 AccountFacade(const QByteArray &instanceIdentifier);
49 virtual ~AccountFacade(); 90 virtual ~AccountFacade();
50 KAsync::Job<void> create(const Sink::ApplicationDomain::SinkAccount &resource) Q_DECL_OVERRIDE;
51 KAsync::Job<void> modify(const Sink::ApplicationDomain::SinkAccount &resource) Q_DECL_OVERRIDE;
52 KAsync::Job<void> remove(const Sink::ApplicationDomain::SinkAccount &resource) Q_DECL_OVERRIDE;
53 QPair<KAsync::Job<void>, typename Sink::ResultEmitter<Sink::ApplicationDomain::SinkAccount::Ptr>::Ptr> load(const Sink::Query &query) Q_DECL_OVERRIDE;
54private:
55 ConfigStore mConfigStore;
56}; 91};
57 92
58class ConfigNotifier : public QObject
59{
60 Q_OBJECT
61public:
62 void add(const Sink::ApplicationDomain::SinkAccount::Ptr &account)
63 {
64 emit added(account);
65 }
66
67 void remove(const Sink::ApplicationDomain::SinkAccount::Ptr &account)
68 {
69 emit removed(account);
70 }
71 93
72 void modify(const Sink::ApplicationDomain::SinkAccount::Ptr &account)
73 {
74 emit modified(account);
75 }
76signals:
77 void added(const Sink::ApplicationDomain::SinkAccount::Ptr &account);
78 void removed(const Sink::ApplicationDomain::SinkAccount::Ptr &account);
79 void modified(const Sink::ApplicationDomain::SinkAccount::Ptr &account);
80};
diff --git a/tests/accountstest.cpp b/tests/accountstest.cpp
index dc74d15..8062bc3 100644
--- a/tests/accountstest.cpp
+++ b/tests/accountstest.cpp
@@ -1,11 +1,13 @@
1#include <QTest> 1#include <QTest>
2#include <QDebug> 2#include <QDebug>
3#include <QSignalSpy> 3#include <QSignalSpy>
4#include <QAbstractItemModel>
4#include <functional> 5#include <functional>
5 6
6#include <test.h> 7#include <test.h>
7#include <store.h> 8#include <store.h>
8#include <log.h> 9#include <log.h>
10#include <configstore.h>
9 11
10class AccountsTest : public QObject 12class AccountsTest : public QObject
11{ 13{
@@ -18,6 +20,12 @@ private slots:
18 Sink::Log::setDebugOutputLevel(Sink::Log::Trace); 20 Sink::Log::setDebugOutputLevel(Sink::Log::Trace);
19 } 21 }
20 22
23 void init()
24 {
25 ConfigStore("accounts").clear();
26 ConfigStore("resources").clear();
27 }
28
21 void testLoad() 29 void testLoad()
22 { 30 {
23 using namespace Sink; 31 using namespace Sink;
@@ -64,6 +72,32 @@ private slots:
64 }) 72 })
65 .exec().waitForFinished(); 73 .exec().waitForFinished();
66 } 74 }
75
76 void testLiveQuery()
77 {
78 using namespace Sink;
79 using namespace Sink::ApplicationDomain;
80
81 auto account = ApplicationDomainType::createEntity<SinkAccount>();
82 account.setProperty("type", "maildir");
83 account.setProperty("name", "name");
84 Store::create(account).exec().waitForFinished();
85
86 Query query;
87 query.liveQuery = true;
88 auto model = Store::loadModel<SinkAccount>(query);
89 QSignalSpy spy(model.data(), &QAbstractItemModel::rowsInserted);
90 QTRY_COMPARE(spy.count(), 1);
91 Store::create(account).exec().waitForFinished();
92 QTRY_COMPARE(spy.count(), 2);
93
94 //Ensure the notifier only affects one type
95 auto resource = ApplicationDomainType::createEntity<SinkResource>();
96 resource.setProperty("type", "org.kde.mailtransport");
97 Store::create(resource).exec().waitForFinished();
98 QTRY_COMPARE(spy.count(), 2);
99 }
100
67}; 101};
68 102
69QTEST_GUILESS_MAIN(AccountsTest) 103QTEST_GUILESS_MAIN(AccountsTest)