From 938c519b026835daca93aee96a1766b4f68de62c Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Wed, 5 Jul 2017 22:40:11 +0200 Subject: Notification printing --- common/notification.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'common') diff --git a/common/notification.cpp b/common/notification.cpp index da31e20..20bc654 100644 --- a/common/notification.cpp +++ b/common/notification.cpp @@ -48,7 +48,7 @@ static QByteArray name(int type) QDebug operator<<(QDebug dbg, const Sink::Notification &n) { - dbg << "Notification(Type:" << name(n.type) << "Id, :" << n.id << ", Code:"; + dbg << "Notification(Type:" << name(n.type) << ", Id:" << n.id << ", Code:"; dbg << n.code; dbg << ", Message:" << n.message << ", Entities:" << n.entities << ")"; return dbg.space(); -- cgit v1.2.3 From 09fba6f07c87aec84c80ce65136f0b7333b0b0bd Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Wed, 5 Jul 2017 23:40:34 +0200 Subject: Keep notifier alive for notifications of new resources. Necessary to get notifications for newly created resources. --- common/notifier.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'common') diff --git a/common/notifier.cpp b/common/notifier.cpp index 1af65e9..1b7cbdb 100644 --- a/common/notifier.cpp +++ b/common/notifier.cpp @@ -49,6 +49,7 @@ public: QList> resourceAccess; QList> handler; + QSharedPointer > > mResourceEmitter; QObject context; }; @@ -91,6 +92,9 @@ Notifier::Notifier(const Sink::Query &resourceQuery) : d(new Sink::Notifier::Pri SinkTraceCtx(resourceCtx) << "Resource query complete"; }); emitter->fetch({}); + if (resourceQuery.liveQuery()) { + d->mResourceEmitter = emitter; + } result.first.exec(); } -- cgit v1.2.3 From a894efcaced8b1e2e330c13d4e8f2d07d3ae814b Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Thu, 6 Jul 2017 21:22:04 +0200 Subject: Avoid regenerating the messageId on every modfication --- common/mailpreprocessor.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'common') diff --git a/common/mailpreprocessor.cpp b/common/mailpreprocessor.cpp index 5c54fbc..253e8b4 100644 --- a/common/mailpreprocessor.cpp +++ b/common/mailpreprocessor.cpp @@ -104,9 +104,6 @@ static void updatedIndexedProperties(Sink::ApplicationDomain::Mail &mail, KMime: mail.setExtractedBcc(getContactList(msg->bcc(true))); mail.setExtractedDate(msg->date(true)->dateTime()); - //The rest should never change, unless we didn't have the headers available initially. - auto messageId = msg->messageID(true)->identifier(); - //Ensure the mssageId is unique. //If there already is one with the same id we'd have to assign a new message id, which probably doesn't make any sense. @@ -125,12 +122,21 @@ static void updatedIndexedProperties(Sink::ApplicationDomain::Mail &mail, KMime: parentMessageId = inReplyTo.first(); } } + + //The rest should never change, unless we didn't have the headers available initially. + auto messageId = msg->messageID(true)->identifier(); if (messageId.isEmpty()) { - auto tmp = KMime::Message::Ptr::create(); - auto header = tmp->messageID(true); - header->generate("kube.kde.org"); - messageId = header->as7BitString(); - SinkWarning() << "Message id is empty, generating one: " << messageId; + //reuse an existing messageis (on modification) + auto existing = mail.getMessageId(); + if (existing.isEmpty()) { + auto tmp = KMime::Message::Ptr::create(); + auto header = tmp->messageID(true); + header->generate("kube.kde.org"); + messageId = header->as7BitString(); + SinkWarning() << "Message id is empty, generating one: " << messageId; + } else { + messageId = existing; + } } mail.setExtractedMessageId(messageId); -- cgit v1.2.3 From 86c75f9d7747353045aae9b141da3f8fb08583f9 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Sun, 16 Jul 2017 00:26:44 +0200 Subject: Filter resources for syncing by type. Otherwise we end up sending sync requests for contacts to imap resources. --- common/store.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'common') diff --git a/common/store.cpp b/common/store.cpp index 4735113..29e826a 100644 --- a/common/store.cpp +++ b/common/store.cpp @@ -311,9 +311,14 @@ KAsync::Job Store::synchronize(const Sink::Query &query) KAsync::Job Store::synchronize(const Sink::SyncScope &scope) { + auto resourceFilter = scope.getResourceFilter(); + //Filter resources by type by default + if (!resourceFilter.propertyFilter.contains(ApplicationDomain::SinkResource::Capabilities::name) && !scope.type().isEmpty()) { + resourceFilter.propertyFilter.insert(ApplicationDomain::SinkResource::Capabilities::name, Query::Comparator{scope.type(), Query::Comparator::Contains}); + } Sink::Query query; - query.setFilter(scope.getResourceFilter()); - SinkLog() << "Synchronizing: " << query; + query.setFilter(resourceFilter); + SinkLog() << "Synchronizing all resource matching: " << query; return fetchAll(query) .template each([scope](const ApplicationDomain::SinkResource::Ptr &resource) -> KAsync::Job { return synchronize(resource->identifier(), scope); -- cgit v1.2.3 From 9448988676c524e4080e18e861dbd7e1def84e44 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Sun, 16 Jul 2017 19:18:59 +0200 Subject: No return value needed here. --- common/storage/entitystore.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'common') diff --git a/common/storage/entitystore.cpp b/common/storage/entitystore.cpp index 22e5ae3..1ac87b7 100644 --- a/common/storage/entitystore.cpp +++ b/common/storage/entitystore.cpp @@ -516,9 +516,8 @@ void EntityStore::readLatest(const QByteArray &type, const QByteArray &uid, cons { auto db = DataStore::mainDatabase(d->getTransaction(), type); db.findLatest(uid, - [=](const QByteArray &key, const QByteArray &value) -> bool { + [=](const QByteArray &key, const QByteArray &value) { callback(DataStore::uidFromKey(key), Sink::EntityBuffer(value.data(), value.size())); - return false; }, [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Error during query: " << error.message << uid; }); } @@ -653,7 +652,7 @@ bool EntityStore::exists(const QByteArray &type, const QByteArray &uid) bool alreadyRemoved = false; DataStore::mainDatabase(d->transaction, type) .findLatest(uid, - [&found, &alreadyRemoved](const QByteArray &key, const QByteArray &data) -> bool { + [&found, &alreadyRemoved](const QByteArray &key, const QByteArray &data) { auto entity = GetEntity(data.data()); if (entity && entity->metadata()) { auto metadata = GetMetadata(entity->metadata()->Data()); @@ -662,7 +661,6 @@ bool EntityStore::exists(const QByteArray &type, const QByteArray &uid) alreadyRemoved = true; } } - return false; }, [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to read old revision from storage: " << error.message; }); if (!found) { -- cgit v1.2.3 From 8fe4eace598997c3ff4c74aa04f723e8ea444239 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Sun, 16 Jul 2017 19:52:23 +0200 Subject: Better error messages --- common/storage_lmdb.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'common') diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp index f7999d1..9fb2feb 100644 --- a/common/storage_lmdb.cpp +++ b/common/storage_lmdb.cpp @@ -406,7 +406,7 @@ int DataStore::NamedDatabase::scan(const QByteArray &k, const std::functionname.toLatin1() + d->db, getErrorCode(rc), QByteArray("Key: ") + k + " : " + QByteArray(mdb_strerror(rc))); + Error error(d->name.toLatin1() + d->db, getErrorCode(rc), QByteArray("Error during scan. Key: ") + k + " : " + QByteArray(mdb_strerror(rc))); errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); } @@ -420,6 +420,11 @@ void DataStore::NamedDatabase::findLatest(const QByteArray &k, const std::functi // Not an error. We rely on this to read nothing from non-existing databases. return; } + if (k.isEmpty()) { + Error error(d->name.toLatin1() + d->db, GenericError, QByteArray("Can't use findLatest with empty key.")); + errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); + return; + } int rc; MDB_val key; @@ -471,10 +476,10 @@ void DataStore::NamedDatabase::findLatest(const QByteArray &k, const std::functi mdb_cursor_close(cursor); if (rc) { - Error error(d->name.toLatin1(), getErrorCode(rc), QByteArray("Key: ") + k + " : " + QByteArray(mdb_strerror(rc))); + Error error(d->name.toLatin1(), getErrorCode(rc), QByteArray("Error during find latest. Key: ") + k + " : " + QByteArray(mdb_strerror(rc))); errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); } else if (!foundValue) { - Error error(d->name.toLatin1(), 1, QByteArray("Key: ") + k + " : No value found"); + Error error(d->name.toLatin1(), 1, QByteArray("Error during find latest. Key: ") + k + " : No value found"); errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); } -- cgit v1.2.3 From 942f5b11f2d91c030a4c87d13f8075538c21abc1 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Sun, 16 Jul 2017 19:53:05 +0200 Subject: Simplified code The while loop is executed at least once, so advanced is always true. --- common/storage_lmdb.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'common') diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp index 9fb2feb..58e3a9a 100644 --- a/common/storage_lmdb.cpp +++ b/common/storage_lmdb.cpp @@ -446,25 +446,23 @@ void DataStore::NamedDatabase::findLatest(const QByteArray &k, const std::functi if ((rc = mdb_cursor_get(cursor, &key, &data, op)) == 0) { // The first lookup will find a key that is equal or greather than our key if (QByteArray::fromRawData((char *)key.mv_data, key.mv_size).startsWith(k)) { - bool advanced = false; + //Read next value until we no longer match while (QByteArray::fromRawData((char *)key.mv_data, key.mv_size).startsWith(k)) { - advanced = true; MDB_cursor_op nextOp = MDB_NEXT; rc = mdb_cursor_get(cursor, &key, &data, nextOp); if (rc) { break; } } - if (advanced) { - MDB_cursor_op prefOp = MDB_PREV; - // We read past the end above, just take the last value - if (rc == MDB_NOTFOUND) { - prefOp = MDB_LAST; - } - rc = mdb_cursor_get(cursor, &key, &data, prefOp); - foundValue = true; - resultHandler(QByteArray::fromRawData((char *)key.mv_data, key.mv_size), QByteArray::fromRawData((char *)data.mv_data, data.mv_size)); + //Now read the previous value, and that's the latest one + MDB_cursor_op prefOp = MDB_PREV; + // We read past the end above, just take the last value + if (rc == MDB_NOTFOUND) { + prefOp = MDB_LAST; } + rc = mdb_cursor_get(cursor, &key, &data, prefOp); + foundValue = true; + resultHandler(QByteArray::fromRawData((char *)key.mv_data, key.mv_size), QByteArray::fromRawData((char *)data.mv_data, data.mv_size)); } } -- cgit v1.2.3 From 431f9a20c849fb438ca743d6989ad768d568e91d Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Sun, 16 Jul 2017 19:53:43 +0200 Subject: Sanity check queries --- common/store.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'common') diff --git a/common/store.cpp b/common/store.cpp index 29e826a..392f9ce 100644 --- a/common/store.cpp +++ b/common/store.cpp @@ -40,6 +40,25 @@ Q_DECLARE_METATYPE(QSharedPointer); Q_DECLARE_METATYPE(std::shared_ptr); + +static bool sanityCheckQuery(const Sink::Query &query) +{ + for (const auto &id : query.ids()) { + if (id.isEmpty()) { + SinkError() << "Empty id in query."; + return false; + } + } + for (const auto &id : query.getResourceFilter().ids) { + if (id.isEmpty()) { + SinkError() << "Empty resourceid in query."; + return false; + } + } + return true; +} + + namespace Sink { QString Store::storageLocation() @@ -138,6 +157,7 @@ static Log::Context getQueryContext(const Sink::Query &query, const QByteArray & template QSharedPointer Store::loadModel(const Query &query) { + Q_ASSERT(sanityCheckQuery(query)); auto ctx = getQueryContext(query, ApplicationDomain::getTypeName()); auto model = QSharedPointer>::create(query, query.requestedProperties, ctx); @@ -342,6 +362,7 @@ KAsync::Job> Store::fetchAll(const Sink::Query & template KAsync::Job> Store::fetch(const Sink::Query &query, int minimumAmount) { + Q_ASSERT(sanityCheckQuery(query)); auto model = loadModel(query); auto list = QSharedPointer>::create(); auto context = QSharedPointer::create(); @@ -393,6 +414,7 @@ DomainType Store::readOne(const Sink::Query &query) template QList Store::read(const Sink::Query &query_) { + Q_ASSERT(sanityCheckQuery(query_)); auto query = query_; query.setFlags(Query::SynchronousQuery); -- cgit v1.2.3 From 5d197dec9dc8f5c8ef4acd72555c32108404c4ae Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Thu, 27 Jul 2017 15:44:32 -0600 Subject: Only print modified properties if we have any. --- common/domain/applicationdomaintype.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'common') diff --git a/common/domain/applicationdomaintype.cpp b/common/domain/applicationdomaintype.cpp index 3718f77..2050fac 100644 --- a/common/domain/applicationdomaintype.cpp +++ b/common/domain/applicationdomaintype.cpp @@ -34,7 +34,13 @@ QDebug Sink::ApplicationDomain::operator<< (QDebug d, const Sink::ApplicationDom QDebug Sink::ApplicationDomain::operator<< (QDebug d, const Sink::ApplicationDomain::ApplicationDomainType &type) { d << "ApplicationDomainType(\n"; - auto properties = type.mAdaptor->availableProperties(); + auto properties = [&] { + if (!type.changedProperties().isEmpty()) { + return type.changedProperties(); + } else { + return type.mAdaptor->availableProperties(); + } + }(); std::sort(properties.begin(), properties.end()); d << " " << "Id: " << "\t" << type.identifier() << "\n"; d << " " << "Resource: " << "\t" << type.resourceInstanceIdentifier() << "\n"; -- cgit v1.2.3 From 63290ccce102495427a26e689725e565a03ae77a Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Thu, 27 Jul 2017 15:45:12 -0600 Subject: Skip modifications that do nothing. This allows us to i.e. blindly mark mails as read in kube, with the modification automatically being dropped if it doesn't do anything useful. --- common/domain/applicationdomaintype.cpp | 9 +++++++-- common/store.cpp | 8 ++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'common') diff --git a/common/domain/applicationdomaintype.cpp b/common/domain/applicationdomaintype.cpp index 2050fac..ee70c35 100644 --- a/common/domain/applicationdomaintype.cpp +++ b/common/domain/applicationdomaintype.cpp @@ -222,8 +222,13 @@ QVariant ApplicationDomainType::getProperty(const QByteArray &key) const void ApplicationDomainType::setProperty(const QByteArray &key, const QVariant &value) { Q_ASSERT(mAdaptor); - mChangeSet->insert(key); - mAdaptor->setProperty(key, value); + auto existing = mAdaptor->getProperty(key); + if (existing.isValid() && existing == value) { + SinkTrace() << "Tried to set property that is still the same: " << key << value; + } else { + mChangeSet->insert(key); + mAdaptor->setProperty(key, value); + } } void ApplicationDomainType::setResource(const QByteArray &identifier) diff --git a/common/store.cpp b/common/store.cpp index 392f9ce..33d7b51 100644 --- a/common/store.cpp +++ b/common/store.cpp @@ -209,6 +209,10 @@ KAsync::Job Store::create(const DomainType &domainObject) template KAsync::Job Store::modify(const DomainType &domainObject) { + if (domainObject.changedProperties().isEmpty()) { + SinkLog() << "Nothing to modify: " << domainObject.identifier(); + return KAsync::null(); + } SinkLog() << "Modify: " << domainObject; // Potentially move to separate thread as well auto facade = getFacade(domainObject.resourceInstanceIdentifier()); @@ -218,6 +222,10 @@ KAsync::Job Store::modify(const DomainType &domainObject) template KAsync::Job Store::modify(const Query &query, const DomainType &domainObject) { + if (domainObject.changedProperties().isEmpty()) { + SinkLog() << "Nothing to modify: " << domainObject.identifier(); + return KAsync::null(); + } SinkLog() << "Modify: " << query << domainObject; return fetchAll(query) .each([=] (const typename DomainType::Ptr &entity) { -- cgit v1.2.3 From 4333b8fe03d50ae9cfd5dfb5c656cc48302dc072 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Thu, 27 Jul 2017 15:54:46 -0600 Subject: Empty resource id's are valid when we search for resources. --- common/store.cpp | 6 ------ 1 file changed, 6 deletions(-) (limited to 'common') diff --git a/common/store.cpp b/common/store.cpp index 33d7b51..b0aac4c 100644 --- a/common/store.cpp +++ b/common/store.cpp @@ -49,12 +49,6 @@ static bool sanityCheckQuery(const Sink::Query &query) return false; } } - for (const auto &id : query.getResourceFilter().ids) { - if (id.isEmpty()) { - SinkError() << "Empty resourceid in query."; - return false; - } - } return true; } -- cgit v1.2.3 From 5275b0f173579162176e2340cbb9eaedafe8334a Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Thu, 27 Jul 2017 16:00:50 -0600 Subject: Adjusted docs and test. --- common/domain/applicationdomaintype.h | 10 ++++++++++ common/store.h | 3 +++ 2 files changed, 13 insertions(+) (limited to 'common') diff --git a/common/domain/applicationdomaintype.h b/common/domain/applicationdomaintype.h index 602d54c..1250455 100644 --- a/common/domain/applicationdomaintype.h +++ b/common/domain/applicationdomaintype.h @@ -270,7 +270,17 @@ public: bool hasProperty(const QByteArray &key) const; QVariant getProperty(const QByteArray &key) const; + + /** + * Set a property and record a changed property + * + * If the propery is available and did not change the call will be ignored. + */ void setProperty(const QByteArray &key, const QVariant &value); + + /** + * Convenience method to set a reference property. + */ void setProperty(const QByteArray &key, const ApplicationDomainType &value); QByteArray getBlobProperty(const QByteArray &key) const; diff --git a/common/store.h b/common/store.h index 34e14df..3ad547e 100644 --- a/common/store.h +++ b/common/store.h @@ -75,12 +75,15 @@ KAsync::Job SINK_EXPORT create(const DomainType &domainObject); * Modify an entity. * * This includes moving etc. since these are also simple settings on a property. + * Note that the modification will be dropped if there is no changedProperty on the domain object. */ template KAsync::Job SINK_EXPORT modify(const DomainType &domainObject); /** * Modify a set of entities identified by @param query. + * + * Note that the modification will be dropped if there is no changedProperty on the domain object. */ template KAsync::Job SINK_EXPORT modify(const Query &query, const DomainType &domainObject); -- cgit v1.2.3 From 9e6952baf64b51fa7ddb6ac91d4ce79ebfd2b2df Mon Sep 17 00:00:00 2001 From: Heiko Becker Date: Sat, 13 May 2017 00:11:45 +0200 Subject: Use imported targets instead of qt5_use_modules From Qt's documentation: "This macro is obsolete. Use target_link_libraries with IMPORTED targets instead." It's only recommended with cmake >=2.8.9 & < 2.8.12. Sink already requires cmake 3.0. One advantage of using the imported targets is, that cmake complains if a target isn't found before it's used, like Qt5Concurrent missing from the find_package_call here. Reviewers: #sink, cmollekopf Reviewed By: #sink, cmollekopf Subscribers: #sink Tags: #sink Differential Revision: https://phabricator.kde.org/D6361 --- common/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'common') diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 001a412..8421fc2 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -116,13 +116,13 @@ SET_TARGET_PROPERTIES(${PROJECT_NAME} EXPORT_NAME ${PROJECT_NAME} ) -qt5_use_modules(${PROJECT_NAME} LINK_PUBLIC Network) -qt5_use_modules(${PROJECT_NAME} LINK_PRIVATE Gui) target_link_libraries(${PROJECT_NAME} PUBLIC KAsync + Qt5::Network PRIVATE ${storage_LIBS} + Qt5::Gui KF5::Mime KF5::Contacts ) -- cgit v1.2.3 From 1c2f876c83afcb6dbbf830b3fe368eab86838552 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Wed, 9 Aug 2017 15:59:31 -0600 Subject: Avoid warning setFuture can emit signals directly if the future is already stopped. This does not apply to our case but it fixes the warning. "QFutureWatcher::connect: connecting after calling setFuture() is likely to produce race" --- common/asyncutils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'common') diff --git a/common/asyncutils.h b/common/asyncutils.h index 67b5928..c80af30 100644 --- a/common/asyncutils.h +++ b/common/asyncutils.h @@ -31,12 +31,12 @@ KAsync::Job run(const std::function &f, bool runAsync = true) return KAsync::start([f](KAsync::Future &future) { auto result = QtConcurrent::run(f); auto watcher = new QFutureWatcher; - watcher->setFuture(result); QObject::connect(watcher, &QFutureWatcher::finished, watcher, [&future, watcher]() { future.setValue(watcher->future().result()); delete watcher; future.setFinished(); }); + watcher->setFuture(result); }); } else { return KAsync::start([f]() { -- cgit v1.2.3 From c22c91df4a35ce8fefad409f1ae265c74b8ede14 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Fri, 11 Aug 2017 20:16:20 -0600 Subject: Cleanup --- common/synchronizer.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'common') diff --git a/common/synchronizer.cpp b/common/synchronizer.cpp index 3b32e68..d6b1c1f 100644 --- a/common/synchronizer.cpp +++ b/common/synchronizer.cpp @@ -593,17 +593,13 @@ KAsync::Job Synchronizer::replay(const QByteArray &type, const QByteArray KAsync::Job job = KAsync::null(); //TODO This requires supporting every domain type here as well. Can we solve this better so we can do the dispatch somewhere centrally? if (type == ApplicationDomain::getTypeName()) { - auto folder = store().readEntity(key); - job = replay(folder, operation, oldRemoteId, modifiedProperties); + job = replay(store().readEntity(key), operation, oldRemoteId, modifiedProperties); } else if (type == ApplicationDomain::getTypeName()) { - auto mail = store().readEntity(key); - job = replay(mail, operation, oldRemoteId, modifiedProperties); + job = replay(store().readEntity(key), operation, oldRemoteId, modifiedProperties); } else if (type == ApplicationDomain::getTypeName()) { - auto mail = store().readEntity(key); - job = replay(mail, operation, oldRemoteId, modifiedProperties); + job = replay(store().readEntity(key), operation, oldRemoteId, modifiedProperties); } else if (type == ApplicationDomain::getTypeName()) { - auto mail = store().readEntity(key); - job = replay(mail, operation, oldRemoteId, modifiedProperties); + job = replay(store().readEntity(key), operation, oldRemoteId, modifiedProperties); } else { SinkErrorCtx(mLogCtx) << "Replayed unknown type: " << type; } -- cgit v1.2.3 From 1e9879479ea22017266de596db61d06d3950941b Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Fri, 11 Aug 2017 20:17:16 -0600 Subject: Silence the compiler warning --- common/domain/applicationdomaintype_p.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'common') diff --git a/common/domain/applicationdomaintype_p.h b/common/domain/applicationdomaintype_p.h index a5a6b1d..a60df38 100644 --- a/common/domain/applicationdomaintype_p.h +++ b/common/domain/applicationdomaintype_p.h @@ -45,5 +45,7 @@ struct TypeHelper { } else { Q_ASSERT(false); } + //Silence compiler warning + return Func{}(std::forward(args...)); } }; -- cgit v1.2.3 From 823d4170fc884d3707425c13198c2973b049c3b3 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Sun, 13 Aug 2017 14:16:00 -0600 Subject: static members can apparently still lead to crashes... --- common/resource.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'common') diff --git a/common/resource.cpp b/common/resource.cpp index 32a92ca..804f0bf 100644 --- a/common/resource.cpp +++ b/common/resource.cpp @@ -56,10 +56,10 @@ class ResourceFactory::Private { public: QByteArrayList capabilities; - static QHash> s_loadedFactories; }; -QHash> ResourceFactory::Private::s_loadedFactories; +typedef QHash> FactoryRegistry; +Q_GLOBAL_STATIC(FactoryRegistry, s_loadedFactories); ResourceFactory::ResourceFactory(QObject *parent, const QByteArrayList &capabilities) : QObject(parent), d(new ResourceFactory::Private) { @@ -73,7 +73,7 @@ ResourceFactory::~ResourceFactory() ResourceFactory *ResourceFactory::load(const QByteArray &resourceName) { - ResourceFactory *factory = Private::s_loadedFactories.value(resourceName); + ResourceFactory *factory = s_loadedFactories->value(resourceName); if (factory) { return factory; } @@ -96,7 +96,7 @@ ResourceFactory *ResourceFactory::load(const QByteArray &resourceName) if (object) { factory = qobject_cast(object); if (factory) { - Private::s_loadedFactories.insert(resourceName, factory); + s_loadedFactories->insert(resourceName, factory); //TODO: Instead of always loading both facades and adaptorfactories into the respective singletons, we could also leave this up to the caller. (ResourceFactory::loadFacades(...)) factory->registerFacades(resourceName, FacadeFactory::instance()); factory->registerAdaptorFactories(resourceName, AdaptorFactoryRegistry::instance()); -- cgit v1.2.3 From 3a7d2e81c7fdc8c2e4b9810065028f4906fc28b3 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Fri, 18 Aug 2017 19:04:54 -0600 Subject: Implemented thread merging It can happen that thread messages are not delivered in order, which means we will have to merge threads once all messages are available. --- common/mail/threadindexer.cpp | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) (limited to 'common') diff --git a/common/mail/threadindexer.cpp b/common/mail/threadindexer.cpp index 4171d85..1401fc8 100644 --- a/common/mail/threadindexer.cpp +++ b/common/mail/threadindexer.cpp @@ -34,9 +34,37 @@ void ThreadIndexer::updateThreadingIndex(const QByteArray &identifier, const App QVector thread; - //a child already registered our thread. + //check if a child already registered our thread. thread = index().secondaryLookup(messageId); + if (!thread.isEmpty()) { + //A child already registered our thread so we merge the childs thread + //* check if we have a parent thread, if not just continue as usual + //* get all messages that have the same threadid as the child + //* switch all to the parents thread + auto parentThread = index().secondaryLookup(parentMessageId); + if (!parentThread.isEmpty()) { + auto childThreadId = thread.first(); + auto parentThreadId = parentThread.first(); + SinkTrace() << "Merging child thread: " << childThreadId << " into parent thread: " << parentThreadId; + + //Ensure this mail ends up in the correct thread + index().unindex(messageId, childThreadId, transaction); + //We have to copy the id here, otherwise it doesn't survive the subsequent writes + thread = QVector() << QByteArray{parentThreadId.data(), parentThreadId.size()}; + + //Merge all child messages into the correct thread + auto childThreadMessageIds = index().secondaryLookup(childThreadId); + for (const auto &msgId : childThreadMessageIds) { + SinkTrace() << "Merging child message: " << msgId; + index().unindex(msgId, childThreadId, transaction); + index().unindex(childThreadId, msgId, transaction); + index().index(msgId, parentThreadId, transaction); + index().index(parentThreadId, msgId, transaction); + } + } + } + //If parent is already available, add to thread of parent if (thread.isEmpty() && parentMessageId.isValid()) { thread = index().secondaryLookup(parentMessageId); -- cgit v1.2.3 From 5ddfe22ebf43763bdf49e639ca9a403e34e84c22 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Tue, 22 Aug 2017 11:06:17 -0600 Subject: Ensure the copied enum matches --- common/store.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'common') diff --git a/common/store.cpp b/common/store.cpp index b0aac4c..1701a43 100644 --- a/common/store.cpp +++ b/common/store.cpp @@ -36,6 +36,17 @@ #include "storage.h" #include "log.h" +#define ASSERT_ENUMS_MATCH(A, B) Q_STATIC_ASSERT_X(static_cast(A) == static_cast(B), "The enum values must match"); + +//Ensure the copied enum matches +typedef ModelResult MailModelResult; +ASSERT_ENUMS_MATCH(Sink::Store::DomainObjectBaseRole, MailModelResult::DomainObjectBaseRole) +ASSERT_ENUMS_MATCH(Sink::Store::ChildrenFetchedRole, MailModelResult::ChildrenFetchedRole) +ASSERT_ENUMS_MATCH(Sink::Store::DomainObjectRole, MailModelResult::DomainObjectRole) +ASSERT_ENUMS_MATCH(Sink::Store::StatusRole, MailModelResult::StatusRole) +ASSERT_ENUMS_MATCH(Sink::Store::WarningRole, MailModelResult::WarningRole) +ASSERT_ENUMS_MATCH(Sink::Store::ProgressRole, MailModelResult::ProgressRole) + Q_DECLARE_METATYPE(QSharedPointer>) Q_DECLARE_METATYPE(QSharedPointer); Q_DECLARE_METATYPE(std::shared_ptr); -- cgit v1.2.3 From ea75d4bdba79d2a879c2ed31564928d4ef3cd9b1 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Fri, 25 Aug 2017 18:21:15 -0600 Subject: Default to NoStatus for resources until we know more. This allows the aggregation to ignore resources where we don't have any status information yet, so the account doesn't always end up being offline. --- common/domain/applicationdomaintype.h | 4 +++- common/resourceaccess.cpp | 2 +- common/synchronizer.cpp | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) (limited to 'common') diff --git a/common/domain/applicationdomaintype.h b/common/domain/applicationdomaintype.h index 1250455..518f6d5 100644 --- a/common/domain/applicationdomaintype.h +++ b/common/domain/applicationdomaintype.h @@ -118,12 +118,14 @@ enum SINK_EXPORT SyncStatus { * The status of an account or resource. * * It is set as follows: - * * By default the status is offline. + * * By default the status is no status. + * * If a connection to the server failed the status is Offline. * * If a connection to the server could be established the status is Connected. * * If an error occurred that keeps the resource from operating (so non transient), the resource enters the error state. * * If a long running operation is started the resource goes to the busy state (and return to the previous state after that). */ enum SINK_EXPORT Status { + NoStatus, OfflineStatus, ConnectedStatus, BusyStatus, diff --git a/common/resourceaccess.cpp b/common/resourceaccess.cpp index 808d892..35fa46c 100644 --- a/common/resourceaccess.cpp +++ b/common/resourceaccess.cpp @@ -232,7 +232,7 @@ KAsync::Job ResourceAccess::Private::initializeSocket() ResourceAccess::ResourceAccess(const QByteArray &resourceInstanceIdentifier, const QByteArray &resourceType) : ResourceAccessInterface(), d(new Private(resourceType, resourceInstanceIdentifier, this)) { - mResourceStatus = Sink::ApplicationDomain::OfflineStatus; + mResourceStatus = Sink::ApplicationDomain::NoStatus; SinkTrace() << "Starting access"; } diff --git a/common/synchronizer.cpp b/common/synchronizer.cpp index d6b1c1f..b6e33d5 100644 --- a/common/synchronizer.cpp +++ b/common/synchronizer.cpp @@ -40,7 +40,7 @@ Synchronizer::Synchronizer(const Sink::ResourceContext &context) mSyncStorage(Sink::storageLocation(), mResourceContext.instanceId() + ".synchronization", Sink::Storage::DataStore::DataStore::ReadWrite), mSyncInProgress(false) { - mCurrentState.push(ApplicationDomain::Status::OfflineStatus); + mCurrentState.push(ApplicationDomain::Status::NoStatus); SinkTraceCtx(mLogCtx) << "Starting synchronizer: " << mResourceContext.resourceType << mResourceContext.instanceId(); } -- cgit v1.2.3 From 7669eeecdd394e1dd9ee8c2fa06edb948a410f6b Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Sat, 26 Aug 2017 09:32:23 -0600 Subject: Only return connected if there is any resource connected. --- common/resourcefacade.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'common') diff --git a/common/resourcefacade.cpp b/common/resourcefacade.cpp index dab6aed..829375c 100644 --- a/common/resourcefacade.cpp +++ b/common/resourcefacade.cpp @@ -391,7 +391,10 @@ QPair, typename Sink::ResultEmitter Date: Mon, 28 Aug 2017 17:19:51 -0600 Subject: Detect connection lost so we can go to offline state kimap should really have better error codes... --- common/domain/applicationdomaintype.h | 1 + common/synchronizer.cpp | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'common') diff --git a/common/domain/applicationdomaintype.h b/common/domain/applicationdomaintype.h index 518f6d5..f7fd07e 100644 --- a/common/domain/applicationdomaintype.h +++ b/common/domain/applicationdomaintype.h @@ -101,6 +101,7 @@ enum SINK_EXPORT ErrorCode { LoginError, ConfigurationError, TransmissionError, + ConnectionLostError, }; enum SINK_EXPORT SuccessCode { diff --git a/common/synchronizer.cpp b/common/synchronizer.cpp index b6e33d5..46d3980 100644 --- a/common/synchronizer.cpp +++ b/common/synchronizer.cpp @@ -344,8 +344,11 @@ void Synchronizer::setStatusFromResult(const KAsync::Error &error, const QString } else if (error.errorCode == ApplicationDomain::LoginError) { //If we failed to login altough we could connect that indicates a problem with our setup. setStatus(ApplicationDomain::ErrorStatus, s, requestId); + } else if (error.errorCode == ApplicationDomain::ConnectionLostError) { + //We've lost the connection so we assume the connection to the server broke. + setStatus(ApplicationDomain::OfflineStatus, s, requestId); } - //We don't know what kind of error this was, so we assume it's transient and don't change ou status. + //We don't know what kind of error this was, so we assume it's transient and don't change our status. } else { //An operation against the server worked, so we're probably online. setStatus(ApplicationDomain::ConnectedStatus, s, requestId); -- cgit v1.2.3 From dd2d4263459c12b9ca65a23711f5f77fe34fef1b Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Mon, 28 Aug 2017 21:19:51 -0600 Subject: use Q_GLOBAL_STATIC In an attempt to resolve T6890. --- common/log.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'common') diff --git a/common/log.cpp b/common/log.cpp index 5dfb872..3edc89a 100644 --- a/common/log.cpp +++ b/common/log.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -267,7 +266,7 @@ public: QSet mDebugAreas; }; -static auto sDebugAreaCollector = std::unique_ptr(new DebugAreaCollector); +Q_GLOBAL_STATIC(DebugAreaCollector, sDebugAreaCollector); QSet Sink::Log::debugAreas() { -- cgit v1.2.3 From d05f3be54f619575769eaf7f00edff9f99feb6f6 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Mon, 28 Aug 2017 21:39:07 -0600 Subject: Avoid use after destruction --- common/log.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'common') diff --git a/common/log.cpp b/common/log.cpp index 3edc89a..ac87c84 100644 --- a/common/log.cpp +++ b/common/log.cpp @@ -270,12 +270,17 @@ Q_GLOBAL_STATIC(DebugAreaCollector, sDebugAreaCollector); QSet Sink::Log::debugAreas() { - return sDebugAreaCollector->debugAreas(); + if (!sDebugAreaCollector.isDestroyed()) { + return sDebugAreaCollector->debugAreas(); + } + return {}; } static void collectDebugArea(const QString &debugArea) { - sDebugAreaCollector->add(debugArea); + if (!sDebugAreaCollector.isDestroyed()) { + sDebugAreaCollector->add(debugArea); + } } static bool containsItemStartingWith(const QByteArray &pattern, const QByteArrayList &list) -- cgit v1.2.3 From 4a1bcc63cd2919f74f29b41c7b000f80da7449f4 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Thu, 31 Aug 2017 13:57:25 -0600 Subject: Avoid non threadsafe initialization. local static initialization is only threadsafe if initialized on construction. The other codepath is not threadsafe, but is only used in testcode. --- common/definitions.cpp | 18 +++++++++++++----- common/definitions.h | 8 +++++++- 2 files changed, 20 insertions(+), 6 deletions(-) (limited to 'common') diff --git a/common/definitions.cpp b/common/definitions.cpp index 17977bc..ee18d52 100644 --- a/common/definitions.cpp +++ b/common/definitions.cpp @@ -39,11 +39,17 @@ QString Sink::storageLocation() return dataLocation() + "/storage"; } +static QString sinkLocation(QStandardPaths::StandardLocation location) +{ + return QStandardPaths::writableLocation(location) + "/sink"; +} + QString Sink::dataLocation() { - static QString location; + static QString location = sinkLocation(QStandardPaths::GenericDataLocation); + //Warning: This is not threadsafe, but clearLocationCache is only ever used in testcode. The initialization above is required to make at least the initialization threadsafe (relies on C++11 threadsafe initialization). if (rereadDataLocation) { - location = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/sink"; + location = sinkLocation(QStandardPaths::GenericDataLocation); rereadDataLocation = false; } return location; @@ -51,9 +57,10 @@ QString Sink::dataLocation() QString Sink::configLocation() { - static QString location; + static QString location = sinkLocation(QStandardPaths::GenericConfigLocation); + //Warning: This is not threadsafe, but clearLocationCache is only ever used in testcode. The initialization above is required to make at least the initialization threadsafe (relies on C++11 threadsafe initialization). if (rereadConfigLocation) { - location = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/sink"; + location = sinkLocation(QStandardPaths::GenericConfigLocation); rereadConfigLocation = false; } return location; @@ -61,8 +68,9 @@ QString Sink::configLocation() QString Sink::temporaryFileLocation() { - static QString location; + static QString location = dataLocation() + "/temporaryFiles"; static bool dirCreated = false; + //Warning: This is not threadsafe, but clearLocationCache is only ever used in testcode. The initialization above is required to make at least the initialization threadsafe (relies on C++11 threadsafe initialization). if (rereadTemporaryFileLocation) { location = dataLocation() + "/temporaryFiles"; dirCreated = QDir{}.mkpath(location); diff --git a/common/definitions.h b/common/definitions.h index ce9e794..7ef215b 100644 --- a/common/definitions.h +++ b/common/definitions.h @@ -25,10 +25,16 @@ #include namespace Sink { -void SINK_EXPORT clearLocationCache(); QString SINK_EXPORT storageLocation(); QString SINK_EXPORT dataLocation(); QString SINK_EXPORT configLocation(); QString SINK_EXPORT temporaryFileLocation(); QString SINK_EXPORT resourceStorageLocation(const QByteArray &resourceInstanceIdentifier); + +/** + * Clear the location cache and lookup locations again. + * + * Warning: Calling this results in non-threadsafe initialization, only use it in test-code. + */ +void SINK_EXPORT clearLocationCache(); } -- cgit v1.2.3 From 6bfcc22e08aebbabeac3e2ccb61556439d9f4b56 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Thu, 31 Aug 2017 14:06:33 -0600 Subject: Use Q_GLOBAL_STATIC for threadsafety. This resolves the following warning on shutdown it seems: "QObject::connect: No such signal QObject::aboutToClose() in ../../include/QtCore/5.9.1/QtCore/private/../../../../../src/corelib/io/qtextstream_p.h:75" --- common/log.cpp | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) (limited to 'common') diff --git a/common/log.cpp b/common/log.cpp index ac87c84..bfc9d5e 100644 --- a/common/log.cpp +++ b/common/log.cpp @@ -26,10 +26,13 @@ static QSettings &config() return *sSettings.localData(); } -static QByteArray sPrimaryComponent; +Q_GLOBAL_STATIC(QByteArray, sPrimaryComponent); + void Sink::Log::setPrimaryComponent(const QString &component) { - sPrimaryComponent = component.toUtf8(); + if (!sPrimaryComponent.isDestroyed()) { + *sPrimaryComponent = component.toUtf8(); + } } class DebugStream : public QIODevice @@ -322,13 +325,17 @@ static QByteArray getFileName(const char *file) static QString assembleDebugArea(const char *debugArea, const char *debugComponent, const char *file) { - if (sPrimaryComponent.isEmpty()) { - sPrimaryComponent = getProgramName(); + if (!sPrimaryComponent.isDestroyed() && sPrimaryComponent->isEmpty()) { + *sPrimaryComponent = getProgramName(); + } + if (!sPrimaryComponent.isDestroyed()) { + //Using stringbuilder for fewer allocations + return QLatin1String{*sPrimaryComponent} % QLatin1String{"."} % + (debugComponent ? (QLatin1String{debugComponent} + QLatin1String{"."}) : QLatin1String{""}) % + (debugArea ? QLatin1String{debugArea} : QLatin1String{getFileName(file)}); + } else { + return {}; } - //Using stringbuilder for fewer allocations - return QLatin1String{sPrimaryComponent} % QLatin1String{"."} % - (debugComponent ? (QLatin1String{debugComponent} + QLatin1String{"."}) : QLatin1String{""}) % - (debugArea ? QLatin1String{debugArea} : QLatin1String{getFileName(file)}); } static bool isFiltered(DebugLevel debugLevel, const QByteArray &fullDebugArea) @@ -350,14 +357,19 @@ bool Sink::Log::isFiltered(DebugLevel debugLevel, const char *debugArea, const c return isFiltered(debugLevel, assembleDebugArea(debugArea, debugComponent, file).toLatin1()); } +Q_GLOBAL_STATIC(NullStream, sNullStream); +Q_GLOBAL_STATIC(DebugStream, sDebugStream); + QDebug Sink::Log::debugStream(DebugLevel debugLevel, int line, const char *file, const char *function, const char *debugArea, const char *debugComponent) { const auto fullDebugArea = assembleDebugArea(debugArea, debugComponent, file); collectDebugArea(fullDebugArea); - static NullStream nullstream; if (isFiltered(debugLevel, fullDebugArea.toLatin1())) { - return QDebug(&nullstream); + if (!sNullStream.isDestroyed()) { + return QDebug(sNullStream); + } + return QDebug{QtDebugMsg}; } QString prefix; @@ -422,8 +434,10 @@ QDebug Sink::Log::debugStream(DebugLevel debugLevel, int line, const char *file, } output += ":"; - static DebugStream stream; - QDebug debug(&stream); + if (sDebugStream.isDestroyed()) { + return QDebug{QtDebugMsg}; + } + QDebug debug(sDebugStream); debug.noquote().nospace() << output; -- cgit v1.2.3 From f3eb97b9b4aabef41d8bc6514c74d85600e60d7f Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Sat, 2 Sep 2017 15:59:51 -0600 Subject: Ensure we monitor resources for status changes that have been created after the query. This fixes status monitoring when creating a new account. --- common/resourcefacade.cpp | 48 +++++++++++++++++++++++++++++++++++------------ common/resourcefacade.h | 1 + 2 files changed, 37 insertions(+), 12 deletions(-) (limited to 'common') diff --git a/common/resourcefacade.cpp b/common/resourcefacade.cpp index 829375c..0687bbc 100644 --- a/common/resourcefacade.cpp +++ b/common/resourcefacade.cpp @@ -24,6 +24,7 @@ #include "store.h" #include "resourceaccess.h" #include "resource.h" +#include "facadefactory.h" using namespace Sink; @@ -358,27 +359,50 @@ QPair, typename Sink::ResultEmitter(query, mIdentifier, mTypeName, sConfigNotifier, ctx); auto monitoredResources = QSharedPointer>::create(); - runner->setStatusUpdater([runner, monitoredResources, ctx](ApplicationDomain::SinkAccount &account) { - Query query; + auto monitorResource = [monitoredResources, runner, ctx] (const QByteArray &accountIdentifier, const ApplicationDomain::SinkResource &resource, const ResourceAccess::Ptr &resourceAccess) { + if (!monitoredResources->contains(resource.identifier())) { + auto ret = QObject::connect(resourceAccess.data(), &ResourceAccess::notification, runner->guard(), [resource, runner, resourceAccess, accountIdentifier, ctx](const Notification ¬ification) { + SinkTraceCtx(ctx) << "Received notification in facade: " << notification.type; + if (notification.type == Notification::Status) { + runner->statusChanged(accountIdentifier); + } + }); + Q_ASSERT(ret); + monitoredResources->insert(resource.identifier()); + } + }; + runner->setStatusUpdater([this, runner, monitoredResources, ctx, monitorResource](ApplicationDomain::SinkAccount &account) { + Query query{Query::LiveQuery}; query.filter(account.identifier()); query.request() .request(); const auto resources = Store::read(query); SinkTraceCtx(ctx) << "Found resource belonging to the account " << account.identifier() << " : " << resources; auto accountIdentifier = account.identifier(); + + //Monitor for new resources so they can be monitored as well + if (!runner->mResourceEmitter.contains(accountIdentifier)) { + auto facade = Sink::FacadeFactory::instance().getFacade(); + Q_ASSERT(facade); + + auto emitter = facade->load(query, ctx).second; + emitter->onAdded([=](const ApplicationDomain::SinkResource::Ptr &resource) { + auto resourceAccess = Sink::ResourceAccessFactory::instance().getAccess(resource->identifier(), ResourceConfig::getResourceType(resource->identifier())); + monitorResource(accountIdentifier, *resource, resourceAccess); + }); + emitter->onModified([](const ApplicationDomain::SinkResource::Ptr &) {}); + emitter->onRemoved([](const ApplicationDomain::SinkResource::Ptr &) {}); + emitter->onInitialResultSetComplete([](const ApplicationDomain::SinkResource::Ptr &, bool) {}); + emitter->onComplete([]() {}); + emitter->fetch({}); + runner->mResourceEmitter[accountIdentifier] = emitter; + } + QList states; + //Gather all resources and ensure they are monitored for (const auto &resource : resources) { auto resourceAccess = ResourceAccessFactory::instance().getAccess(resource.identifier(), ResourceConfig::getResourceType(resource.identifier())); - if (!monitoredResources->contains(resource.identifier())) { - auto ret = QObject::connect(resourceAccess.data(), &ResourceAccess::notification, runner->guard(), [resource, runner, resourceAccess, accountIdentifier, ctx](const Notification ¬ification) { - SinkTraceCtx(ctx) << "Received notification in facade: " << notification.type; - if (notification.type == Notification::Status) { - runner->statusChanged(accountIdentifier); - } - }); - Q_ASSERT(ret); - monitoredResources->insert(resource.identifier()); - } + monitorResource(accountIdentifier, resource, resourceAccess); states << resourceAccess->getResourceStatus(); } const auto status = [&] { diff --git a/common/resourcefacade.h b/common/resourcefacade.h index 76fadce..36049c4 100644 --- a/common/resourcefacade.h +++ b/common/resourcefacade.h @@ -65,6 +65,7 @@ public: void setStatusUpdater(const std::function &); void statusChanged(const QByteArray &identifier); QObject *guard() const; + QMap > > > mResourceEmitter; private: void updateStatus(DomainType &entity); -- cgit v1.2.3 From 32c507fa0565547a187632db8a80c07babb95d9d Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Sat, 2 Sep 2017 16:00:46 -0600 Subject: Avoid hiding the index() function --- common/modelresult.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'common') diff --git a/common/modelresult.cpp b/common/modelresult.cpp index 58703ab..95f4643 100644 --- a/common/modelresult.cpp +++ b/common/modelresult.cpp @@ -298,16 +298,16 @@ void ModelResult::add(const Ptr &value) auto parent = createIndexFromId(id); SinkTraceCtx(mLogCtx) << "Added entity " << childId << "id: " << value->identifier() << "parent: " << id; const auto keys = mTree[id]; - int index = 0; - for (; index < keys.size(); index++) { - if (childId < keys.at(index)) { + int idx = 0; + for (; idx < keys.size(); idx++) { + if (childId < keys.at(idx)) { break; } } // SinkTraceCtx(mLogCtx) << "Inserting rows " << index << parent; - beginInsertRows(parent, index, index); + beginInsertRows(parent, idx, idx); mEntities.insert(childId, value); - mTree[id].insert(index, childId); + mTree[id].insert(idx, childId); mParents.insert(childId, id); endInsertRows(); // SinkTraceCtx(mLogCtx) << "Inserted rows " << mTree[id].size(); -- cgit v1.2.3 From a98311fbc807b83ecfc65a17f98464e5f1f9b3f8 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Thu, 7 Sep 2017 02:34:10 +0200 Subject: Fixed getUids by type filtering. We used to simply return all uids. Requires "sinksh upgrade" --- common/storage.h | 6 +++--- common/storage/entitystore.cpp | 6 +++--- common/storage_common.cpp | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) (limited to 'common') diff --git a/common/storage.h b/common/storage.h index 8c129df..c39b904 100644 --- a/common/storage.h +++ b/common/storage.h @@ -198,9 +198,9 @@ public: static QByteArray getTypeFromRevision(const Transaction &, qint64 revision); static void recordRevision(Transaction &, qint64 revision, const QByteArray &uid, const QByteArray &type); static void removeRevision(Transaction &, qint64 revision); - static void recordUid(DataStore::Transaction &transaction, const QByteArray &uid); - static void removeUid(DataStore::Transaction &transaction, const QByteArray &uid); - static void getUids(const Transaction &, const std::function &); + static void recordUid(DataStore::Transaction &transaction, const QByteArray &uid, const QByteArray &type); + static void removeUid(DataStore::Transaction &transaction, const QByteArray &uid, const QByteArray &type); + static void getUids(const QByteArray &type, const Transaction &, const std::function &); bool exists() const; diff --git a/common/storage/entitystore.cpp b/common/storage/entitystore.cpp index 1ac87b7..5514e31 100644 --- a/common/storage/entitystore.cpp +++ b/common/storage/entitystore.cpp @@ -267,7 +267,7 @@ bool EntityStore::add(const QByteArray &type, ApplicationDomain::ApplicationDoma [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to write entity" << entity.identifier() << newRevision; }); DataStore::setMaxRevision(d->transaction, newRevision); DataStore::recordRevision(d->transaction, newRevision, entity.identifier(), type); - DataStore::recordUid(d->transaction, entity.identifier()); + DataStore::recordUid(d->transaction, entity.identifier(), type); SinkTraceCtx(d->logCtx) << "Wrote entity: " << entity.identifier() << type << newRevision; return true; } @@ -379,7 +379,7 @@ bool EntityStore::remove(const QByteArray &type, const Sink::ApplicationDomain:: [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to write entity" << uid << newRevision; }); DataStore::setMaxRevision(d->transaction, newRevision); DataStore::recordRevision(d->transaction, newRevision, uid, type); - DataStore::removeUid(d->transaction, uid); + DataStore::removeUid(d->transaction, uid, type); return true; } @@ -638,7 +638,7 @@ ApplicationDomain::ApplicationDomainType EntityStore::readPrevious(const QByteAr void EntityStore::readAllUids(const QByteArray &type, const std::function callback) { - DataStore::getUids(d->getTransaction(), callback); + DataStore::getUids(type, d->getTransaction(), callback); } bool EntityStore::contains(const QByteArray &type, const QByteArray &uid) diff --git a/common/storage_common.cpp b/common/storage_common.cpp index 8603787..630dae9 100644 --- a/common/storage_common.cpp +++ b/common/storage_common.cpp @@ -156,19 +156,19 @@ void DataStore::removeRevision(DataStore::Transaction &transaction, qint64 revis transaction.openDatabase("revisionType").remove(QByteArray::number(revision)); } -void DataStore::recordUid(DataStore::Transaction &transaction, const QByteArray &uid) +void DataStore::recordUid(DataStore::Transaction &transaction, const QByteArray &uid, const QByteArray &type) { - transaction.openDatabase("uids").write(uid, ""); + transaction.openDatabase(type + "uids").write(uid, ""); } -void DataStore::removeUid(DataStore::Transaction &transaction, const QByteArray &uid) +void DataStore::removeUid(DataStore::Transaction &transaction, const QByteArray &uid, const QByteArray &type) { - transaction.openDatabase("uids").remove(uid); + transaction.openDatabase(type + "uids").remove(uid); } -void DataStore::getUids(const Transaction &transaction, const std::function &callback) +void DataStore::getUids(const QByteArray &type, const Transaction &transaction, const std::function &callback) { - transaction.openDatabase("uids").scan("", [&] (const QByteArray &key, const QByteArray &) { + transaction.openDatabase(type + "uids").scan("", [&] (const QByteArray &key, const QByteArray &) { callback(key); return true; }); -- cgit v1.2.3