summaryrefslogtreecommitdiffstats
path: root/common/store.cpp
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2016-02-10 10:47:12 +0100
committerChristian Mollekopf <chrigi_1@fastmail.fm>2016-02-10 10:47:12 +0100
commit0991561c9630fcb89b9666b8d57c2b7ea592fec6 (patch)
tree66e12af97c055a62fc15ef1946e3bb166d4c55bf /common/store.cpp
parentc1c336a9064557bb987c30582bce84bab3f869bc (diff)
downloadsink-0991561c9630fcb89b9666b8d57c2b7ea592fec6.tar.gz
sink-0991561c9630fcb89b9666b8d57c2b7ea592fec6.zip
Moved Store to separate file
Diffstat (limited to 'common/store.cpp')
-rw-r--r--common/store.cpp259
1 files changed, 259 insertions, 0 deletions
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