summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/CMakeLists.txt2
-rw-r--r--common/clientapi.cpp210
-rw-r--r--common/clientapi.h64
-rw-r--r--common/store.cpp259
-rw-r--r--common/store.h101
5 files changed, 365 insertions, 271 deletions
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index a165820..02335ad 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -33,6 +33,7 @@ set(storage_SRCS storage_lmdb.cpp)
33set(storage_LIBS lmdb) 33set(storage_LIBS lmdb)
34 34
35set(command_SRCS 35set(command_SRCS
36 store.cpp
36 modelresult.cpp 37 modelresult.cpp
37 definitions.cpp 38 definitions.cpp
38 log.cpp 39 log.cpp
@@ -102,6 +103,7 @@ install(TARGETS ${PROJECT_NAME}
102add_clang_static_analysis(${PROJECT_NAME}) 103add_clang_static_analysis(${PROJECT_NAME})
103 104
104install(FILES 105install(FILES
106 store.h
105 clientapi.h 107 clientapi.h
106 domain/applicationdomaintype.h 108 domain/applicationdomaintype.h
107 query.h 109 query.h
diff --git a/common/clientapi.cpp b/common/clientapi.cpp
index cbbfdd8..01411c2 100644
--- a/common/clientapi.cpp
+++ b/common/clientapi.cpp
@@ -46,115 +46,6 @@
46namespace Sink 46namespace Sink
47{ 47{
48 48
49QString Store::storageLocation()
50{
51 return Sink::storageLocation();
52}
53
54static QList<QByteArray> getResources(const QList<QByteArray> &resourceFilter, const QByteArray &type)
55{
56 //Return the global resource (signified by an empty name) for types that don't eblong to a specific resource
57 if (type == "sinkresource") {
58 return QList<QByteArray>() << "";
59 }
60 QList<QByteArray> resources;
61 const auto configuredResources = ResourceConfig::getResources();
62 if (resourceFilter.isEmpty()) {
63 for (const auto &res : configuredResources.keys()) {
64 //TODO filter by entity type
65 resources << res;
66 }
67 } else {
68 for (const auto &res : resourceFilter) {
69 if (configuredResources.contains(res)) {
70 resources << res;
71 } else {
72 qWarning() << "Resource is not existing: " << res;
73 }
74 }
75 }
76 Trace() << "Found resources: " << resources;
77 return resources;
78}
79
80template <class DomainType>
81QSharedPointer<QAbstractItemModel> Store::loadModel(Query query)
82{
83 Trace() << "Query: ";
84 Trace() << " Requested: " << query.requestedProperties;
85 Trace() << " Filter: " << query.propertyFilter;
86 Trace() << " Parent: " << query.parentProperty;
87 Trace() << " Ids: " << query.ids;
88 Trace() << " IsLive: " << query.liveQuery;
89 auto model = QSharedPointer<ModelResult<DomainType, typename DomainType::Ptr> >::create(query, query.requestedProperties);
90
91 //* Client defines lifetime of model
92 //* The model lifetime defines the duration of live-queries
93 //* The facade needs to life for the duration of any calls being made (assuming we get rid of any internal callbacks
94 //* The emitter needs to live or the duration of query (respectively, the model)
95 //* The result provider needs to live for as long as results are provided (until the last thread exits).
96
97 // Query all resources and aggregate results
98 auto resources = getResources(query.resources, ApplicationDomain::getTypeName<DomainType>());
99 auto aggregatingEmitter = AggregatingResultEmitter<typename DomainType::Ptr>::Ptr::create();
100 model->setEmitter(aggregatingEmitter);
101 KAsync::iterate(resources)
102 .template each<void, QByteArray>([query, aggregatingEmitter](const QByteArray &resource, KAsync::Future<void> &future) {
103 auto facade = FacadeFactory::instance().getFacade<DomainType>(resourceName(resource), resource);
104 if (facade) {
105 Trace() << "Trying to fetch from resource " << resource;
106 auto result = facade->load(query);
107 aggregatingEmitter->addEmitter(result.second);
108 result.first.template then<void>([&future](){future.setFinished();}).exec();
109 } else {
110 Trace() << "Couldn' find a facade for " << resource;
111 //Ignore the error and carry on
112 future.setFinished();
113 }
114 }).exec();
115 model->fetchMore(QModelIndex());
116
117 return model;
118}
119
120template <class DomainType>
121static std::shared_ptr<StoreFacade<DomainType> > getFacade(const QByteArray &resourceInstanceIdentifier)
122{
123 if (auto facade = FacadeFactory::instance().getFacade<DomainType>(resourceName(resourceInstanceIdentifier), resourceInstanceIdentifier)) {
124 return facade;
125 }
126 return std::make_shared<NullFacade<DomainType> >();
127}
128
129template <class DomainType>
130KAsync::Job<void> Store::create(const DomainType &domainObject) {
131 //Potentially move to separate thread as well
132 auto facade = getFacade<DomainType>(domainObject.resourceInstanceIdentifier());
133 return facade->create(domainObject).template then<void>([facade](){}, [](int errorCode, const QString &error) {
134 Warning() << "Failed to create";
135 });
136}
137
138template <class DomainType>
139KAsync::Job<void> Store::modify(const DomainType &domainObject)
140{
141 //Potentially move to separate thread as well
142 auto facade = getFacade<DomainType>(domainObject.resourceInstanceIdentifier());
143 return facade->modify(domainObject).template then<void>([facade](){}, [](int errorCode, const QString &error) {
144 Warning() << "Failed to modify";
145 });
146}
147
148template <class DomainType>
149KAsync::Job<void> Store::remove(const DomainType &domainObject)
150{
151 //Potentially move to separate thread as well
152 auto facade = getFacade<DomainType>(domainObject.resourceInstanceIdentifier());
153 return facade->remove(domainObject).template then<void>([facade](){}, [](int errorCode, const QString &error) {
154 Warning() << "Failed to remove";
155 });
156}
157
158KAsync::Job<void> ResourceControl::shutdown(const QByteArray &identifier) 49KAsync::Job<void> ResourceControl::shutdown(const QByteArray &identifier)
159{ 50{
160 Trace() << "shutdown " << identifier; 51 Trace() << "shutdown " << identifier;
@@ -190,37 +81,6 @@ KAsync::Job<void> ResourceControl::start(const QByteArray &identifier)
190 }); 81 });
191} 82}
192 83
193KAsync::Job<void> Store::removeDataFromDisk(const QByteArray &identifier)
194{
195 //All databases are going to become invalid, nuke the environments
196 //TODO: all clients should react to a notification the resource
197 Sink::Storage::clearEnv();
198 Trace() << "Remove data from disk " << identifier;
199 auto time = QSharedPointer<QTime>::create();
200 time->start();
201 auto resourceAccess = QSharedPointer<Sink::ResourceAccess>::create(identifier);
202 resourceAccess->open();
203 return resourceAccess->sendCommand(Sink::Commands::RemoveFromDiskCommand).then<void>([resourceAccess, time]() {
204 Trace() << "Remove from disk complete." << Log::TraceTime(time->elapsed());
205 });
206}
207
208KAsync::Job<void> Store::synchronize(const Sink::Query &query)
209{
210 Trace() << "synchronize" << query.resources;
211 return KAsync::iterate(query.resources)
212 .template each<void, QByteArray>([query](const QByteArray &resource, KAsync::Future<void> &future) {
213 Trace() << "Synchronizing " << resource;
214 auto resourceAccess = QSharedPointer<Sink::ResourceAccess>::create(resource);
215 resourceAccess->open();
216 resourceAccess->synchronizeResource(true, false).then<void>([&future, resourceAccess]() {
217 future.setFinished();
218 }).exec();
219 })
220 //FIXME JOBAPI this is only required because we don't care about the return value of each (and each shouldn't even have a return value)
221 .template then<void>([](){});
222}
223
224KAsync::Job<void> ResourceControl::flushMessageQueue(const QByteArrayList &resourceIdentifier) 84KAsync::Job<void> ResourceControl::flushMessageQueue(const QByteArrayList &resourceIdentifier)
225{ 85{
226 Trace() << "flushMessageQueue" << resourceIdentifier; 86 Trace() << "flushMessageQueue" << resourceIdentifier;
@@ -243,67 +103,6 @@ KAsync::Job<void> ResourceControl::flushReplayQueue(const QByteArrayList &resour
243} 103}
244 104
245template <class DomainType> 105template <class DomainType>
246KAsync::Job<DomainType> Store::fetchOne(const Sink::Query &query)
247{
248 return KAsync::start<DomainType>([query](KAsync::Future<DomainType> &future) {
249 //FIXME We could do this more elegantly if composed jobs would have the correct type (In that case we'd simply return the value from then continuation, and could avoid the outer job entirely)
250 fetch<DomainType>(query, 1)
251 .template then<void, QList<typename DomainType::Ptr> >([&future](const QList<typename DomainType::Ptr> &list){
252 future.setValue(*list.first());
253 future.setFinished();
254 }, [&future](int errorCode, const QString &errorMessage) {
255 future.setError(errorCode, errorMessage);
256 future.setFinished();
257 }).exec();
258 });
259}
260
261template <class DomainType>
262KAsync::Job<QList<typename DomainType::Ptr> > Store::fetchAll(const Sink::Query &query)
263{
264 return fetch<DomainType>(query);
265}
266
267template <class DomainType>
268KAsync::Job<QList<typename DomainType::Ptr> > Store::fetch(const Sink::Query &query, int minimumAmount)
269{
270 auto model = loadModel<DomainType>(query);
271 auto list = QSharedPointer<QList<typename DomainType::Ptr> >::create();
272 auto context = QSharedPointer<QObject>::create();
273 return KAsync::start<QList<typename DomainType::Ptr> >([model, list, context, minimumAmount](KAsync::Future<QList<typename DomainType::Ptr> > &future) {
274 if (model->rowCount() >= 1) {
275 for (int i = 0; i < model->rowCount(); i++) {
276 list->append(model->index(i, 0, QModelIndex()).data(Sink::Store::DomainObjectRole).template value<typename DomainType::Ptr>());
277 }
278 } else {
279 QObject::connect(model.data(), &QAbstractItemModel::rowsInserted, context.data(), [model, &future, list](const QModelIndex &index, int start, int end) {
280 for (int i = start; i <= end; i++) {
281 list->append(model->index(i, 0, QModelIndex()).data(Sink::Store::DomainObjectRole).template value<typename DomainType::Ptr>());
282 }
283 });
284 QObject::connect(model.data(), &QAbstractItemModel::dataChanged, context.data(), [model, &future, list, minimumAmount](const QModelIndex &, const QModelIndex &, const QVector<int> &roles) {
285 if (roles.contains(ModelResult<DomainType, typename DomainType::Ptr>::ChildrenFetchedRole)) {
286 if (list->size() < minimumAmount) {
287 future.setError(1, "Not enough values.");
288 } else {
289 future.setValue(*list);
290 }
291 future.setFinished();
292 }
293 });
294 }
295 if (model->data(QModelIndex(), ModelResult<DomainType, typename DomainType::Ptr>::ChildrenFetchedRole).toBool()) {
296 if (list->size() < minimumAmount) {
297 future.setError(1, "Not enough values.");
298 } else {
299 future.setValue(*list);
300 }
301 future.setFinished();
302 }
303 });
304}
305
306template <class DomainType>
307KAsync::Job<void> ResourceControl::inspect(const Inspection &inspectionCommand) 106KAsync::Job<void> ResourceControl::inspect(const Inspection &inspectionCommand)
308{ 107{
309 auto resource = inspectionCommand.resourceIdentifier; 108 auto resource = inspectionCommand.resourceIdentifier;
@@ -371,14 +170,7 @@ void Notifier::registerHandler(std::function<void(const Notification &)> handler
371 d->handler << handler; 170 d->handler << handler;
372} 171}
373 172
374#define REGISTER_TYPE(T) template KAsync::Job<void> Store::remove<T>(const T &domainObject); \ 173#define REGISTER_TYPE(T) template KAsync::Job<void> ResourceControl::inspect<T>(const Inspection &); \
375 template KAsync::Job<void> Store::create<T>(const T &domainObject); \
376 template KAsync::Job<void> Store::modify<T>(const T &domainObject); \
377 template QSharedPointer<QAbstractItemModel> Store::loadModel<T>(Query query); \
378 template KAsync::Job<void> ResourceControl::inspect<T>(const Inspection &); \
379 template KAsync::Job<T> Store::fetchOne<T>(const Query &); \
380 template KAsync::Job<QList<T::Ptr> > Store::fetchAll<T>(const Query &); \
381 template KAsync::Job<QList<T::Ptr> > Store::fetch<T>(const Query &, int); \
382 174
383REGISTER_TYPE(ApplicationDomain::Event); 175REGISTER_TYPE(ApplicationDomain::Event);
384REGISTER_TYPE(ApplicationDomain::Mail); 176REGISTER_TYPE(ApplicationDomain::Mail);
diff --git a/common/clientapi.h b/common/clientapi.h
index 2b49826..a1d2469 100644
--- a/common/clientapi.h
+++ b/common/clientapi.h
@@ -26,6 +26,8 @@
26 26
27#include <Async/Async> 27#include <Async/Async>
28 28
29#include "store.h"
30
29#include "query.h" 31#include "query.h"
30#include "inspection.h" 32#include "inspection.h"
31#include "applicationdomaintype.h" 33#include "applicationdomaintype.h"
@@ -49,68 +51,6 @@ private:
49}; 51};
50 52
51 53
52/**
53 * Store interface used in the client API.
54 */
55namespace Store {
56
57QString SINK_EXPORT storageLocation();
58
59enum Roles {
60 DomainObjectRole = Qt::UserRole + 1, //Must be the same as in ModelResult
61 ChildrenFetchedRole,
62 DomainObjectBaseRole
63};
64
65/**
66 * Asynchronusly load a dataset with tree structure information
67 */
68template <class DomainType>
69QSharedPointer<QAbstractItemModel> SINK_EXPORT loadModel(Query query);
70
71/**
72 * Create a new entity.
73 */
74template <class DomainType>
75KAsync::Job<void> SINK_EXPORT create(const DomainType &domainObject);
76
77/**
78 * Modify an entity.
79 *
80 * This includes moving etc. since these are also simple settings on a property.
81 */
82template <class DomainType>
83KAsync::Job<void> SINK_EXPORT modify(const DomainType &domainObject);
84
85/**
86 * Remove an entity.
87 */
88template <class DomainType>
89KAsync::Job<void> SINK_EXPORT remove(const DomainType &domainObject);
90
91/**
92 * Synchronize data to local cache.
93 */
94KAsync::Job<void> SINK_EXPORT synchronize(const Sink::Query &query);
95
96/**
97 * Removes all resource data from disk.
98 *
99 * This will not touch the configuration. All commands that that arrived at the resource before this command will be dropped. All commands that arrived later will be executed.
100 */
101KAsync::Job<void> SINK_EXPORT removeDataFromDisk(const QByteArray &resourceIdentifier);
102
103template <class DomainType>
104KAsync::Job<DomainType> SINK_EXPORT fetchOne(const Sink::Query &query);
105
106template <class DomainType>
107KAsync::Job<QList<typename DomainType::Ptr> > SINK_EXPORT fetchAll(const Sink::Query &query);
108
109template <class DomainType>
110KAsync::Job<QList<typename DomainType::Ptr> > SINK_EXPORT fetch(const Sink::Query &query, int minimumAmount = 0);
111
112};
113
114namespace ResourceControl { 54namespace ResourceControl {
115 55
116template <class DomainType> 56template <class DomainType>
diff --git a/common/store.cpp b/common/store.cpp
new file mode 100644
index 0000000..7f93202
--- /dev/null
+++ b/common/store.cpp
@@ -0,0 +1,259 @@
1/*
2 * Copyright (C) 2015 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) version 3, or any
8 * later version accepted by the membership of KDE e.V. (or its
9 * successor approved by the membership of KDE e.V.), which shall
10 * act as a proxy defined in Section 6 of version 3 of the license.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include "store.h"
22
23#include <QTime>
24#include <QAbstractItemModel>
25#include <functional>
26#include <memory>
27
28#include "resourceaccess.h"
29#include "commands.h"
30#include "resourcefacade.h"
31#include "definitions.h"
32#include "resourceconfig.h"
33#include "facadefactory.h"
34#include "modelresult.h"
35#include "storage.h"
36#include "log.h"
37
38#undef DEBUG_AREA
39#define DEBUG_AREA "client.store"
40
41namespace Sink
42{
43
44QString Store::storageLocation()
45{
46 return Sink::storageLocation();
47}
48
49static QList<QByteArray> getResources(const QList<QByteArray> &resourceFilter, const QByteArray &type)
50{
51 //Return the global resource (signified by an empty name) for types that don't eblong to a specific resource
52 if (type == "sinkresource") {
53 return QList<QByteArray>() << "";
54 }
55 QList<QByteArray> resources;
56 const auto configuredResources = ResourceConfig::getResources();
57 if (resourceFilter.isEmpty()) {
58 for (const auto &res : configuredResources.keys()) {
59 //TODO filter by entity type
60 resources << res;
61 }
62 } else {
63 for (const auto &res : resourceFilter) {
64 if (configuredResources.contains(res)) {
65 resources << res;
66 } else {
67 qWarning() << "Resource is not existing: " << res;
68 }
69 }
70 }
71 Trace() << "Found resources: " << resources;
72 return resources;
73}
74
75template <class DomainType>
76QSharedPointer<QAbstractItemModel> Store::loadModel(Query query)
77{
78 Trace() << "Query: ";
79 Trace() << " Requested: " << query.requestedProperties;
80 Trace() << " Filter: " << query.propertyFilter;
81 Trace() << " Parent: " << query.parentProperty;
82 Trace() << " Ids: " << query.ids;
83 Trace() << " IsLive: " << query.liveQuery;
84 auto model = QSharedPointer<ModelResult<DomainType, typename DomainType::Ptr> >::create(query, query.requestedProperties);
85
86 //* Client defines lifetime of model
87 //* The model lifetime defines the duration of live-queries
88 //* The facade needs to life for the duration of any calls being made (assuming we get rid of any internal callbacks
89 //* The emitter needs to live or the duration of query (respectively, the model)
90 //* The result provider needs to live for as long as results are provided (until the last thread exits).
91
92 // Query all resources and aggregate results
93 auto resources = getResources(query.resources, ApplicationDomain::getTypeName<DomainType>());
94 auto aggregatingEmitter = AggregatingResultEmitter<typename DomainType::Ptr>::Ptr::create();
95 model->setEmitter(aggregatingEmitter);
96 KAsync::iterate(resources)
97 .template each<void, QByteArray>([query, aggregatingEmitter](const QByteArray &resource, KAsync::Future<void> &future) {
98 auto facade = FacadeFactory::instance().getFacade<DomainType>(resourceName(resource), resource);
99 if (facade) {
100 Trace() << "Trying to fetch from resource " << resource;
101 auto result = facade->load(query);
102 aggregatingEmitter->addEmitter(result.second);
103 result.first.template then<void>([&future](){future.setFinished();}).exec();
104 } else {
105 Trace() << "Couldn' find a facade for " << resource;
106 //Ignore the error and carry on
107 future.setFinished();
108 }
109 }).exec();
110 model->fetchMore(QModelIndex());
111
112 return model;
113}
114
115template <class DomainType>
116static std::shared_ptr<StoreFacade<DomainType> > getFacade(const QByteArray &resourceInstanceIdentifier)
117{
118 if (auto facade = FacadeFactory::instance().getFacade<DomainType>(resourceName(resourceInstanceIdentifier), resourceInstanceIdentifier)) {
119 return facade;
120 }
121 return std::make_shared<NullFacade<DomainType> >();
122}
123
124template <class DomainType>
125KAsync::Job<void> Store::create(const DomainType &domainObject) {
126 //Potentially move to separate thread as well
127 auto facade = getFacade<DomainType>(domainObject.resourceInstanceIdentifier());
128 return facade->create(domainObject).template then<void>([facade](){}, [](int errorCode, const QString &error) {
129 Warning() << "Failed to create";
130 });
131}
132
133template <class DomainType>
134KAsync::Job<void> Store::modify(const DomainType &domainObject)
135{
136 //Potentially move to separate thread as well
137 auto facade = getFacade<DomainType>(domainObject.resourceInstanceIdentifier());
138 return facade->modify(domainObject).template then<void>([facade](){}, [](int errorCode, const QString &error) {
139 Warning() << "Failed to modify";
140 });
141}
142
143template <class DomainType>
144KAsync::Job<void> Store::remove(const DomainType &domainObject)
145{
146 //Potentially move to separate thread as well
147 auto facade = getFacade<DomainType>(domainObject.resourceInstanceIdentifier());
148 return facade->remove(domainObject).template then<void>([facade](){}, [](int errorCode, const QString &error) {
149 Warning() << "Failed to remove";
150 });
151}
152
153KAsync::Job<void> Store::removeDataFromDisk(const QByteArray &identifier)
154{
155 //All databases are going to become invalid, nuke the environments
156 //TODO: all clients should react to a notification the resource
157 Sink::Storage::clearEnv();
158 Trace() << "Remove data from disk " << identifier;
159 auto time = QSharedPointer<QTime>::create();
160 time->start();
161 auto resourceAccess = QSharedPointer<Sink::ResourceAccess>::create(identifier);
162 resourceAccess->open();
163 return resourceAccess->sendCommand(Sink::Commands::RemoveFromDiskCommand).then<void>([resourceAccess, time]() {
164 Trace() << "Remove from disk complete." << Log::TraceTime(time->elapsed());
165 });
166}
167
168KAsync::Job<void> Store::synchronize(const Sink::Query &query)
169{
170 Trace() << "synchronize" << query.resources;
171 return KAsync::iterate(query.resources)
172 .template each<void, QByteArray>([query](const QByteArray &resource, KAsync::Future<void> &future) {
173 Trace() << "Synchronizing " << resource;
174 auto resourceAccess = QSharedPointer<Sink::ResourceAccess>::create(resource);
175 resourceAccess->open();
176 resourceAccess->synchronizeResource(true, false).then<void>([&future, resourceAccess]() {
177 future.setFinished();
178 }).exec();
179 })
180 //FIXME JOBAPI this is only required because we don't care about the return value of each (and each shouldn't even have a return value)
181 .template then<void>([](){});
182}
183
184template <class DomainType>
185KAsync::Job<DomainType> Store::fetchOne(const Sink::Query &query)
186{
187 return KAsync::start<DomainType>([query](KAsync::Future<DomainType> &future) {
188 //FIXME We could do this more elegantly if composed jobs would have the correct type (In that case we'd simply return the value from then continuation, and could avoid the outer job entirely)
189 fetch<DomainType>(query, 1)
190 .template then<void, QList<typename DomainType::Ptr> >([&future](const QList<typename DomainType::Ptr> &list){
191 future.setValue(*list.first());
192 future.setFinished();
193 }, [&future](int errorCode, const QString &errorMessage) {
194 future.setError(errorCode, errorMessage);
195 future.setFinished();
196 }).exec();
197 });
198}
199
200template <class DomainType>
201KAsync::Job<QList<typename DomainType::Ptr> > Store::fetchAll(const Sink::Query &query)
202{
203 return fetch<DomainType>(query);
204}
205
206template <class DomainType>
207KAsync::Job<QList<typename DomainType::Ptr> > Store::fetch(const Sink::Query &query, int minimumAmount)
208{
209 auto model = loadModel<DomainType>(query);
210 auto list = QSharedPointer<QList<typename DomainType::Ptr> >::create();
211 auto context = QSharedPointer<QObject>::create();
212 return KAsync::start<QList<typename DomainType::Ptr> >([model, list, context, minimumAmount](KAsync::Future<QList<typename DomainType::Ptr> > &future) {
213 if (model->rowCount() >= 1) {
214 for (int i = 0; i < model->rowCount(); i++) {
215 list->append(model->index(i, 0, QModelIndex()).data(Sink::Store::DomainObjectRole).template value<typename DomainType::Ptr>());
216 }
217 } else {
218 QObject::connect(model.data(), &QAbstractItemModel::rowsInserted, context.data(), [model, &future, list](const QModelIndex &index, int start, int end) {
219 for (int i = start; i <= end; i++) {
220 list->append(model->index(i, 0, QModelIndex()).data(Sink::Store::DomainObjectRole).template value<typename DomainType::Ptr>());
221 }
222 });
223 QObject::connect(model.data(), &QAbstractItemModel::dataChanged, context.data(), [model, &future, list, minimumAmount](const QModelIndex &, const QModelIndex &, const QVector<int> &roles) {
224 if (roles.contains(ModelResult<DomainType, typename DomainType::Ptr>::ChildrenFetchedRole)) {
225 if (list->size() < minimumAmount) {
226 future.setError(1, "Not enough values.");
227 } else {
228 future.setValue(*list);
229 }
230 future.setFinished();
231 }
232 });
233 }
234 if (model->data(QModelIndex(), ModelResult<DomainType, typename DomainType::Ptr>::ChildrenFetchedRole).toBool()) {
235 if (list->size() < minimumAmount) {
236 future.setError(1, "Not enough values.");
237 } else {
238 future.setValue(*list);
239 }
240 future.setFinished();
241 }
242 });
243}
244
245#define REGISTER_TYPE(T) template KAsync::Job<void> Store::remove<T>(const T &domainObject); \
246 template KAsync::Job<void> Store::create<T>(const T &domainObject); \
247 template KAsync::Job<void> Store::modify<T>(const T &domainObject); \
248 template QSharedPointer<QAbstractItemModel> Store::loadModel<T>(Query query); \
249 template KAsync::Job<T> Store::fetchOne<T>(const Query &); \
250 template KAsync::Job<QList<T::Ptr> > Store::fetchAll<T>(const Query &); \
251 template KAsync::Job<QList<T::Ptr> > Store::fetch<T>(const Query &, int); \
252
253REGISTER_TYPE(ApplicationDomain::Event);
254REGISTER_TYPE(ApplicationDomain::Mail);
255REGISTER_TYPE(ApplicationDomain::Folder);
256REGISTER_TYPE(ApplicationDomain::SinkResource);
257
258} // namespace Sink
259
diff --git a/common/store.h b/common/store.h
new file mode 100644
index 0000000..6696833
--- /dev/null
+++ b/common/store.h
@@ -0,0 +1,101 @@
1/*
2 * Copyright (C) 2015 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) version 3, or any
8 * later version accepted by the membership of KDE e.V. (or its
9 * successor approved by the membership of KDE e.V.), which shall
10 * act as a proxy defined in Section 6 of version 3 of the license.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#pragma once
22
23#include "sink_export.h"
24#include <QString>
25#include <QSharedPointer>
26
27#include <Async/Async>
28
29#include "query.h"
30#include "applicationdomaintype.h"
31
32class QAbstractItemModel;
33
34namespace Sink {
35
36/**
37 * The unified Sink Store.
38 *
39 * This is the primary interface for clients to interact with Sink.
40 * It provides a unified store where all data provided by various resources can be accessed and modified.
41 */
42namespace Store {
43
44QString SINK_EXPORT storageLocation();
45
46enum Roles {
47 DomainObjectRole = Qt::UserRole + 1, //Must be the same as in ModelResult
48 ChildrenFetchedRole,
49 DomainObjectBaseRole
50};
51
52/**
53 * Asynchronusly load a dataset with tree structure information
54 */
55template <class DomainType>
56QSharedPointer<QAbstractItemModel> SINK_EXPORT loadModel(Query query);
57
58/**
59 * Create a new entity.
60 */
61template <class DomainType>
62KAsync::Job<void> SINK_EXPORT create(const DomainType &domainObject);
63
64/**
65 * Modify an entity.
66 *
67 * This includes moving etc. since these are also simple settings on a property.
68 */
69template <class DomainType>
70KAsync::Job<void> SINK_EXPORT modify(const DomainType &domainObject);
71
72/**
73 * Remove an entity.
74 */
75template <class DomainType>
76KAsync::Job<void> SINK_EXPORT remove(const DomainType &domainObject);
77
78/**
79 * Synchronize data to local cache.
80 */
81KAsync::Job<void> SINK_EXPORT synchronize(const Sink::Query &query);
82
83/**
84 * Removes all resource data from disk.
85 *
86 * This will not touch the configuration. All commands that that arrived at the resource before this command will be dropped. All commands that arrived later will be executed.
87 */
88KAsync::Job<void> SINK_EXPORT removeDataFromDisk(const QByteArray &resourceIdentifier);
89
90template <class DomainType>
91KAsync::Job<DomainType> SINK_EXPORT fetchOne(const Sink::Query &query);
92
93template <class DomainType>
94KAsync::Job<QList<typename DomainType::Ptr> > SINK_EXPORT fetchAll(const Sink::Query &query);
95
96template <class DomainType>
97KAsync::Job<QList<typename DomainType::Ptr> > SINK_EXPORT fetch(const Sink::Query &query, int minimumAmount = 0);
98
99 }
100}
101