#include "syncmanager.h" Q_LOGGING_CATEGORY(syncmanager, "sync.manager"); SyncManager::SyncManager(bool dryMode) :mDryMode(dryMode) { } bool SyncManager::databaseInitialization() { database = std::make_shared(&dbMan, mDryMode); // Open database if (!database->open()) { qCritical(syncmanager) << "Not possible to open database:" << dbMan.database().hostName(); return false; } // Get last synchronizaiton date qint64 lastsync_in_ms = database->getLastSync(); if (lastsync_in_ms == -1) { qCritical(syncmanager) << "Not possible to get last synchronization date"; return false; } // here -> nothing can fail mlastSync.setTimestamp(lastsync_in_ms); colnod = std::make_shared(&reqMan, mDryMode); return true; } bool SyncManager::startSynchronization() { // Need current time from db server qint64 syncBegin = database->getNow(); if (syncBegin == -1) { qCritical(syncmanager) << "Not possible to get last sync date"; return false; } qInfo(syncmanager) << tab << "- begin sync ( previous sync" << mlastSync.toDateString() << ")"; // GETTING DATA FROM DATABASE auto databaseData = database->downloadData(); if (!databaseData) { qCritical(syncmanager) << "not possible to get data from database"; return false; } // NOW I HAVE DATA FROM DATABASE colnod->downloadData(mlastSync.timestamp()); connect(colnod.get(), &ColnodAPI::downloadDataFinished, [=](const std::shared_ptr &colnodData) { // NOW I HAVE DATA FROM COLNOD if (!colnodData) { qCritical(syncmanager) << "Not possible to get correct data from colnod"; QCoreApplication::exit(-1); return; } std::unique_ptr preparedData = prepareData(databaseData, colnodData); if (!preparedData) { qCritical(syncmanager) << "Not possible to compose data"; QCoreApplication::exit(-1); return; } if (!database->processData(preparedData->colnodData)) { qCritical(syncmanager) << "Not possible to upload data to database"; } // data is processed to other sides connect(colnod.get(), &ColnodAPI::dataProcessFinished, [&,syncBegin](bool success){ if (!success) { qCritical(syncmanager) << "Not possible to upload data to colnod"; } // REUPDATE tokens pushed from database to colnod (cause of lastUpdate time) Timestamp time; time.setTimestamp(syncBegin); // download data changes in this sync auto tokensRequest = colnod->getTokensUpdated(time); // updated data are ready connect(tokensRequest.get(), &serversdk::Request::finished, [=](){ auto uploadedData = std::make_shared(); uploadedData->loadTokensFromJson(tokensRequest->responseData()); if (!uploadedData) { qCritical(syncmanager) << "Not possible to get correct data from colnod"; QCoreApplication::exit(-1); return; } if (!database->uploadTokens(uploadedData->updatedTokens, {"lastModified"})) { qCritical(syncmanager) << "Not possible to upload data to database"; QCoreApplication::exit(-1); return; } database->reupdateDeleted(databaseData->deletedTokens); if (!database->isValid() || !colnod->isValid()) { qWarning(syncmanager) << "Unsuccessful FINISH, check warnings"; } else { // set new time of last sync to this time of this run if (!database->setLastSync(syncBegin)) { qWarning(syncmanager) << "Unsuccessful FINISH, check warnings"; } qInfo(syncmanager) << "Successful FINISH"; } QCoreApplication::exit(); }); }); colnod->processData(preparedData->databaseData); }); return true; } // goes through downloaded data and check if consist conflicts (both sides edit), already sync/deleted tokens or new tokens std::unique_ptr SyncManager::prepareData(const std::shared_ptr &databaseData, const std::shared_ptr &colnodData) { auto preparedColnodData = std::make_shared(); auto preparedDatabaseData = std::make_shared(); QList conflicts; QList alreadySynced; for (const auto &databaseToken : databaseData->updatedTokens) { if (databaseToken->name().isEmpty()) { initNewToken(databaseToken); } // restriction cauesed by colnod rest api if (databaseToken->description() != "mobile") { qCritical(syncmanager) << "Token:" << databaseToken->id() << "doesn't have 'mobile' in description, can't be "; continue; } // no conflict if (!colnodData->updatedTokens.contains(databaseToken->id())) { preparedDatabaseData->updatedTokens.insert(databaseToken->id(), databaseToken); continue; } // database change is newer if (databaseToken > colnodData->updatedTokens.value(databaseToken->id())) { preparedDatabaseData->updatedTokens.insert(databaseToken->id(), databaseToken); } } preparedDatabaseData->deletedTokens = databaseData->deletedTokens; qInfo(syncmanager) << tab << "- prep db:" << tab << "tokens [" << preparedDatabaseData->updatedTokens.size() << ":" << preparedDatabaseData->deletedTokens.size() << "] [ added/updated : deleted ]"; // add all data which should be pushed to database for (const auto &colnodToken : colnodData->updatedTokens) { // not conflict so add to prepered tokens if (!databaseData->updatedTokens.contains(colnodToken->id())) { auto token = database->getToken(colnodToken->id()); if (!token) { qCritical(syncmanager) << "Not possible to access database."; return nullptr; } if ( token ) { // alredy synced ( tokens are same ) if (token == colnodToken) { alreadySynced.append(colnodToken->id()); continue; } } preparedColnodData->updatedTokens.insert(colnodToken->id(), colnodToken); continue; } // change in colnod is newer if (databaseData->updatedTokens.value(colnodToken->id()) < colnodToken) { preparedColnodData->updatedTokens.insert(colnodToken->id(), colnodToken); } else { conflicts.append(colnodToken->id()); } } for (const auto &id : colnodData->deletedTokens) { auto corespondingToken = database->getToken(id); if ( !corespondingToken) { qWarning(syncmanager) << "Token:" << id << ", deleted in colnod, but not exists in db"; continue; } if ( !corespondingToken->deleted()) { preparedColnodData->deletedTokens.append(id); } } preparedColnodData->updatedSubjects = colnodData->updatedSubjects; preparedColnodData->deletedSubjects = colnodData->deletedSubjects; qInfo(syncmanager) << tab << "- prep col:" << tab << "tokens [" << preparedColnodData->updatedTokens.size() << ":" << preparedColnodData->deletedTokens.size() << "] subjects [" << preparedColnodData->updatedSubjects.size() << ":" << preparedColnodData->deletedSubjects.size() << "] [ added/updated : deleted ]"; auto preparedData = std::make_unique(preparedDatabaseData, preparedColnodData); return preparedData; } void SyncManager::initNewToken(const std::shared_ptr& token) { Q_ASSERT(database); Q_ASSERT(token); qint16 num = database->getNumOfTokensOfSubject(token->subjectId()); token->setName(database->getSubjectNameByTokenId(token->id()) + "_mobile_" + QString::number(num + 1)); token->setDescription("mobile"); token->setTokenData(token->tokenData().toUpper()); qInfo(syncmanager).noquote() << Message::massageTemplate("Inicializing new", "token", "sync manager", token->name()); }