summaryrefslogtreecommitdiffstats
path: root/common/store.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'common/store.cpp')
-rw-r--r--common/store.cpp257
1 files changed, 257 insertions, 0 deletions
diff --git a/common/store.cpp b/common/store.cpp
new file mode 100644
index 0000000..6f080b5
--- /dev/null
+++ b/common/store.cpp
@@ -0,0 +1,257 @@
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}
181
182template <class DomainType>
183KAsync::Job<DomainType> Store::fetchOne(const Sink::Query &query)
184{
185 return KAsync::start<DomainType>([query](KAsync::Future<DomainType> &future) {
186 //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)
187 fetch<DomainType>(query, 1)
188 .template then<void, QList<typename DomainType::Ptr> >([&future](const QList<typename DomainType::Ptr> &list){
189 future.setValue(*list.first());
190 future.setFinished();
191 }, [&future](int errorCode, const QString &errorMessage) {
192 future.setError(errorCode, errorMessage);
193 future.setFinished();
194 }).exec();
195 });
196}
197
198template <class DomainType>
199KAsync::Job<QList<typename DomainType::Ptr> > Store::fetchAll(const Sink::Query &query)
200{
201 return fetch<DomainType>(query);
202}
203
204template <class DomainType>
205KAsync::Job<QList<typename DomainType::Ptr> > Store::fetch(const Sink::Query &query, int minimumAmount)
206{
207 auto model = loadModel<DomainType>(query);
208 auto list = QSharedPointer<QList<typename DomainType::Ptr> >::create();
209 auto context = QSharedPointer<QObject>::create();
210 return KAsync::start<QList<typename DomainType::Ptr> >([model, list, context, minimumAmount](KAsync::Future<QList<typename DomainType::Ptr> > &future) {
211 if (model->rowCount() >= 1) {
212 for (int i = 0; i < model->rowCount(); i++) {
213 list->append(model->index(i, 0, QModelIndex()).data(Sink::Store::DomainObjectRole).template value<typename DomainType::Ptr>());
214 }
215 } else {
216 QObject::connect(model.data(), &QAbstractItemModel::rowsInserted, context.data(), [model, &future, list](const QModelIndex &index, int start, int end) {
217 for (int i = start; i <= end; i++) {
218 list->append(model->index(i, 0, QModelIndex()).data(Sink::Store::DomainObjectRole).template value<typename DomainType::Ptr>());
219 }
220 });
221 QObject::connect(model.data(), &QAbstractItemModel::dataChanged, context.data(), [model, &future, list, minimumAmount](const QModelIndex &, const QModelIndex &, const QVector<int> &roles) {
222 if (roles.contains(ModelResult<DomainType, typename DomainType::Ptr>::ChildrenFetchedRole)) {
223 if (list->size() < minimumAmount) {
224 future.setError(1, "Not enough values.");
225 } else {
226 future.setValue(*list);
227 }
228 future.setFinished();
229 }
230 });
231 }
232 if (model->data(QModelIndex(), ModelResult<DomainType, typename DomainType::Ptr>::ChildrenFetchedRole).toBool()) {
233 if (list->size() < minimumAmount) {
234 future.setError(1, "Not enough values.");
235 } else {
236 future.setValue(*list);
237 }
238 future.setFinished();
239 }
240 });
241}
242
243#define REGISTER_TYPE(T) template KAsync::Job<void> Store::remove<T>(const T &domainObject); \
244 template KAsync::Job<void> Store::create<T>(const T &domainObject); \
245 template KAsync::Job<void> Store::modify<T>(const T &domainObject); \
246 template QSharedPointer<QAbstractItemModel> Store::loadModel<T>(Query query); \
247 template KAsync::Job<T> Store::fetchOne<T>(const Query &); \
248 template KAsync::Job<QList<T::Ptr> > Store::fetchAll<T>(const Query &); \
249 template KAsync::Job<QList<T::Ptr> > Store::fetch<T>(const Query &, int); \
250
251REGISTER_TYPE(ApplicationDomain::Event);
252REGISTER_TYPE(ApplicationDomain::Mail);
253REGISTER_TYPE(ApplicationDomain::Folder);
254REGISTER_TYPE(ApplicationDomain::SinkResource);
255
256} // namespace Sink
257