1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-07-03 23:50:33 +02:00
Files
joplin/QtClient/JoplinQtClient/synchronizer.cpp

322 lines
8.0 KiB
C++
Raw Normal View History

2016-12-27 21:25:07 +01:00
#include "synchronizer.h"
#include "models/folder.h"
#include "models/note.h"
2017-01-05 23:54:13 +01:00
#include "settings.h"
2016-12-27 21:25:07 +01:00
using namespace jop;
2017-03-01 21:01:03 +00:00
Synchronizer::Synchronizer() {
2017-01-05 18:59:01 +01:00
state_ = Idle;
uploadsRemaining_ = 0;
2016-12-27 21:25:07 +01:00
connect(&api_, SIGNAL(requestDone(QJsonObject,QString)), this, SLOT(api_requestDone(QJsonObject,QString)));
}
void Synchronizer::start() {
if (state_ == Frozen) {
qWarning() << "Cannot start synchronizer while frozen";
return;
}
2017-01-05 18:59:01 +01:00
if (state_ != Idle) {
qWarning() << "Cannot start synchronizer because synchronization already in progress. State: " << state_;
return;
}
2017-03-01 21:01:03 +00:00
emit started();
2017-01-12 18:04:21 +01:00
qInfo() << "Starting synchronizer...";
2016-12-27 21:25:07 +01:00
2017-01-09 12:13:58 +01:00
switchState(UploadingChanges);
}
2017-01-05 18:59:01 +01:00
2017-01-09 12:13:58 +01:00
void Synchronizer::setSessionId(const QString &v) {
api_.setSessionId(v);
}
2017-01-05 18:59:01 +01:00
void Synchronizer::abort() {
switchState(Aborting);
}
void Synchronizer::freeze() {
switchState(Frozen);
}
void Synchronizer::unfreeze() {
switchState(Idle);
}
WebApi &Synchronizer::api() {
return api_;
}
2017-01-09 12:13:58 +01:00
QUrlQuery Synchronizer::valuesToUrlQuery(const QHash<QString, Change::Value>& values) const {
QUrlQuery query;
for (QHash<QString, Change::Value>::const_iterator it = values.begin(); it != values.end(); ++it) {
2017-03-01 21:01:03 +00:00
if (it.key() == "id") continue;
2017-01-09 12:13:58 +01:00
query.addQueryItem(it.key(), it.value().toString());
}
return query;
}
2017-01-05 18:59:01 +01:00
2017-01-09 12:13:58 +01:00
void Synchronizer::checkNextState() {
2017-03-01 21:01:03 +00:00
qDebug() << "Synchronizer::checkNextState from state" << state_;
2017-01-09 12:13:58 +01:00
switch (state_) {
2017-01-02 16:28:03 +01:00
2017-03-01 21:01:03 +00:00
case UploadingChanges:
2017-01-03 19:42:01 +01:00
2017-03-01 21:01:03 +00:00
if (uploadsRemaining_ < 0) qCritical() << "Mismatch on upload operations done" << uploadsRemaining_;
2017-01-03 19:42:01 +01:00
2017-01-09 12:13:58 +01:00
if (uploadsRemaining_ <= 0) {
uploadsRemaining_ = 0;
switchState(DownloadingChanges);
}
2017-01-03 19:42:01 +01:00
2017-03-01 21:01:03 +00:00
break;
2017-01-03 19:42:01 +01:00
2017-03-01 21:01:03 +00:00
case DownloadingChanges:
2017-01-03 19:42:01 +01:00
2017-03-01 21:01:03 +00:00
switchState(Idle);
emit finished();
break;
2017-01-03 19:42:01 +01:00
2017-03-01 21:01:03 +00:00
case Idle:
2017-01-03 19:42:01 +01:00
2017-03-01 21:01:03 +00:00
break;
2017-01-05 18:59:01 +01:00
2017-03-01 21:01:03 +00:00
case Aborting:
2017-03-01 21:01:03 +00:00
switchState(Idle);
emit finished();
break;
2017-03-01 21:01:03 +00:00
case Frozen:
2017-03-01 21:01:03 +00:00
break;
2017-03-01 21:01:03 +00:00
default:
2017-01-05 18:59:01 +01:00
2017-03-01 21:01:03 +00:00
qCritical() << "Synchronizer has invalid state" << state_;
break;
2017-01-02 16:28:03 +01:00
2017-01-03 19:42:01 +01:00
}
2016-12-27 21:25:07 +01:00
}
2017-01-09 12:13:58 +01:00
void Synchronizer::switchState(Synchronizer::SynchronizationState state) {
if (state_ == state) {
qCritical() << "Trying to switch synchronizer to its current state" << state;
return;
}
state_ = state;
2017-01-12 18:04:21 +01:00
qInfo() << "Switching synchronizer state to" << state;
2017-01-09 12:13:58 +01:00
if (state == Idle) {
// =============================================================================================
// IDLE STATE
// =============================================================================================
} else if (state == UploadingChanges) {
// =============================================================================================
// UPLOADING STATE
// =============================================================================================
2017-03-01 21:01:03 +00:00
std::vector<Change*> changes = Change::all();
Change::mergedChanges(changes);
2017-01-09 12:13:58 +01:00
uploadsRemaining_ = changes.size();
2017-03-01 21:01:03 +00:00
for (size_t i = 0; i < changes.size(); i++) {
Change* change = changes[i];
jop::Table itemType = (jop::Table)change->value("item_type").toInt();
QString itemId = change->value("item_id").toString();
Change::Type type = (Change::Type)change->value("type").toInt();
qDebug() << "Change" << change->idString() << itemId << itemType;
2017-01-09 12:13:58 +01:00
2017-03-01 21:01:03 +00:00
if (itemType == jop::FoldersTable) {
2017-01-09 12:13:58 +01:00
2017-03-01 21:01:03 +00:00
if (type == Change::Create) {
2017-01-09 12:13:58 +01:00
2017-03-01 21:01:03 +00:00
Folder folder;
folder.load(itemId);
QUrlQuery data = valuesToUrlQuery(folder.values());
api_.put("folders/" + folder.idString(), QUrlQuery(), data, "upload:putFolder:" + folder.idString());
2017-01-09 12:13:58 +01:00
2017-03-01 21:01:03 +00:00
} else if (type == Change::Update) {
2017-01-09 12:13:58 +01:00
2017-03-01 21:01:03 +00:00
Folder folder;
folder.load(itemId);
QStringList mergedFields = change->mergedFields();
QUrlQuery data;
foreach (QString field, mergedFields) {
data.addQueryItem(field, folder.value(field).toString());
}
api_.patch("folders/" + folder.idString(), QUrlQuery(), data, "upload:patchFolder:" + folder.idString());
2017-01-09 12:13:58 +01:00
2017-03-01 21:01:03 +00:00
} else if (type == Change::Delete) {
2017-01-09 12:13:58 +01:00
2017-03-01 21:01:03 +00:00
api_.del("folders/" + itemId, QUrlQuery(), QUrlQuery(), "upload:deleteFolder:" + itemId);
2017-01-09 12:13:58 +01:00
2017-03-01 21:01:03 +00:00
}
} else {
qFatal("Unsupported item type: %d", itemType);
}
}
2017-02-06 20:20:30 +00:00
2017-03-01 21:01:03 +00:00
for (size_t i = 0; i < changes.size(); i++) {
delete changes[i];
}
changes.clear();
2017-01-09 12:13:58 +01:00
checkNextState();
} else if (state_ == DownloadingChanges) {
// =============================================================================================
// DOWNLOADING STATE
// =============================================================================================
Settings settings;
QString lastRevId = settings.value("lastRevId", "0").toString();
QUrlQuery query;
2017-06-03 23:33:46 +01:00
query.addQueryItem("rev_id", lastRevId);
2017-01-09 12:13:58 +01:00
api_.get("synchronizer", query, QUrlQuery(), "download:getSynchronizer");
} else if (state == Aborting) {
// =============================================================================================
// ABORTING STATE
// =============================================================================================
uploadsRemaining_ = 0;
api_.abortAll();
checkNextState();
} else if (state == Frozen) {
// =============================================================================================
// FROZEN STATE
// =============================================================================================
2017-01-09 12:13:58 +01:00
}
2017-01-05 23:54:13 +01:00
2017-01-05 18:59:01 +01:00
}
2017-01-03 19:42:01 +01:00
2017-01-05 18:59:01 +01:00
void Synchronizer::api_requestDone(const QJsonObject& response, const QString& tag) {
if (state_ == Frozen) {
qWarning() << "Receiving response while synchronizer is frozen";
return;
}
2016-12-27 21:25:07 +01:00
QStringList parts = tag.split(':');
2017-01-05 18:59:01 +01:00
QString category = parts[0];
QString action = parts[1];
2017-01-05 23:54:13 +01:00
QString arg1 = "";
QString arg2 = "";
2016-12-27 21:25:07 +01:00
2017-01-05 23:54:13 +01:00
if (parts.size() == 3) arg1 = parts[2];
if (parts.size() == 4) arg2 = parts[3];
2016-12-27 21:25:07 +01:00
2017-01-12 18:04:21 +01:00
qInfo() << "WebApi: done" << category << action << arg1 << arg2;
2017-01-05 18:59:01 +01:00
2017-01-11 15:18:56 +01:00
QString error = "";
if (response.contains("error")) {
error = response.value("error").toString();
qCritical().noquote() << "Sync error:" << error;
// Each action might handle errors differently so let it proceed below
}
2017-01-03 19:42:01 +01:00
2017-01-09 12:13:58 +01:00
// =============================================================================================
// HANDLE UPLOAD RESPONSE
// =============================================================================================
if (state_ == UploadingChanges) {
2017-01-05 18:59:01 +01:00
uploadsRemaining_--;
2017-01-03 19:42:01 +01:00
2017-01-11 15:18:56 +01:00
if (error == "") {
2017-01-12 18:04:21 +01:00
qInfo() << "Synced folder" << arg1;
2017-01-03 19:42:01 +01:00
2017-01-11 15:18:56 +01:00
if (action == "putFolder") {
Change::disposeByItemId(arg1);
}
2017-01-05 18:59:01 +01:00
2017-01-11 15:18:56 +01:00
if (action == "patchFolder") {
Change::disposeByItemId(arg1);
}
2016-12-27 21:25:07 +01:00
2017-01-11 15:18:56 +01:00
if (action == "deleteFolder") {
Change::disposeByItemId(arg1);
}
2017-01-05 18:59:01 +01:00
2017-01-11 15:18:56 +01:00
if (uploadsRemaining_ < 0) {
qWarning() << "Mismatch on operations done:" << uploadsRemaining_;
}
2017-01-05 18:59:01 +01:00
}
2017-01-09 12:13:58 +01:00
checkNextState();
// =============================================================================================
// HANDLE DOWNLOAD RESPONSE
// =============================================================================================
} else if (state_ == DownloadingChanges) {
2017-01-11 15:18:56 +01:00
if (error != "") {
checkNextState();
} else {
if (action == "getSynchronizer") {
QJsonArray items = response["items"].toArray();
QString maxRevId = "";
foreach (QJsonValue it, items) {
QJsonObject obj = it.toObject();
QString itemId = obj["item_id"].toString();
QString itemType = obj["item_type"].toString();
QString operationType = obj["type"].toString();
QString revId = obj["id"].toString();
QJsonObject item = obj["item"].toObject();
if (itemType == "folder") {
if (operationType == "create") {
Folder folder;
folder.loadJsonObject(item);
folder.save(false);
}
if (operationType == "update") {
Folder folder;
folder.load(itemId);
folder.patchJsonObject(item);
folder.save(false);
}
if (operationType == "delete") {
Folder folder;
folder.load(itemId);
folder.dispose();
}
2017-01-09 14:34:06 +01:00
}
2017-01-09 22:14:32 +01:00
2017-01-11 15:18:56 +01:00
if (revId > maxRevId) maxRevId = revId;
2017-01-09 12:13:58 +01:00
}
2017-01-11 15:18:56 +01:00
if (maxRevId != "") {
Settings settings;
settings.setValue("lastRevId", maxRevId);
}
2017-01-07 10:40:13 +01:00
2017-01-11 15:18:56 +01:00
checkNextState();
2017-01-05 18:59:01 +01:00
}
}
} else {
qCritical() << "Invalid category" << category;
2016-12-27 21:25:07 +01:00
}
}