#include "colnodapi.h" #include Q_LOGGING_CATEGORY(colnod, "colnod.api"); ColnodAPI::ColnodAPI(serversdk::RequestManager* reqMan, const bool &dryMode, QString userName, QString password) :mReqMan(reqMan), mUserName(std::move(userName)), mPassword(std::move(password)), mDryMode(dryMode) { // ----------------------------------- mReqMan->setIgnoreSsl(true); // ----------------------------------- } /** * @brief Downloads new data from Colnod -> emits downloadDataFinished signal * @param time * * @return signal with nullptr when something went wrong * @return signal with data when valid data returned */ void ColnodAPI::downloadData(const qint64 &time) { // Have to get Login auto login = requestLogin(); Q_ASSERT(login); connect(login.get(), &serversdk::Request::finished, this, [=](){ // Get login token if (login->status() != 200) { emit downloadDataFinished(nullptr); return; } AuthToken token; auto responseData = login->responseData(); if (responseData.isEmpty()) { qCWarning(colnod) << "empty response data when logging in"; emit downloadDataFinished(nullptr); return; } token.fromJson(responseData); authToken = token.id(); if (authToken.isEmpty()) { qCCritical(colnod) << "Token uuid is empty"; emit downloadDataFinished(nullptr); return; } // NOTE: why? // auto errorHandler = [=](const std::shared_ptr& request){ // if (!checkRequest(request)) { // qWarning(colnod) << "ERROR during calling" << request->url() << ", request unsuccessful"; // mValid = false; // emit downloadDataFinished(nullptr); // TODO: think about it, will the other if passed? // } // else if (!checkResponse(request->responseData())) { // qWarning(colnod) << "Data from" << request->url() << "are invalid"; // mValid = false; // } // }; // if I have all data emit downloadDataFinished connect(this, &ColnodAPI::partOfDataReady, this, &ColnodAPI::areDataReady); Timestamp timestamp; timestamp.setTimestamp(time); auto data = std::make_shared(); int dataCount = 0; auto checkData = [&dataCount, &data, this]() { qDebug() << "check data" << dataCount; if (dataCount > 5) { qCCritical(colnod) << "check data cannnot be higher then 4"; } Q_ASSERT(dataCount < 5); if (dataCount == 4) emit downloadDataFinished(data); }; auto tokensRequest = getTokensUpdated(timestamp); // connect(tokensRequest.get(), &serversdk::Request::finished, errorHandler); connect(tokensRequest.get(), &serversdk::Request::finished, [=, &dataCount](){ qDebug(colnod) << "tokens finished"; if (!saveUpdatedTokens(data, tokensRequest->responseData())) { emit downloadDataFinished(nullptr); return; } ++dataCount; checkData(); //saveUpdatedTokens(data, tokensRequest->responseData()); //emit partOfDataReady(data); }); auto deletedTokensRequest = getTokensDeleted(timestamp); // connect(deletedTokensRequest.get(), &serversdk::Request::finished, errorHandler); connect(deletedTokensRequest.get(), &serversdk::Request::finished, [=, &dataCount](){ qDebug(colnod) << "tokens finished"; if (!saveDeletedTokens(data, deletedTokensRequest->responseData())) { emit downloadDataFinished(nullptr); return; } ++dataCount; checkData(); //emit partOfDataReady(data); }); auto subjectsRequest = getSubjectsUpdated(timestamp); // connect(subjectsRequest.get(), &serversdk::Request::finished, errorHandler); connect(subjectsRequest.get(), &serversdk::Request::finished, [=, &dataCount](){ qDebug(colnod) << "tokens finished"; if (!saveUpdatedSubjects(data, subjectsRequest->responseData())) { emit downloadDataFinished(nullptr); return; } ++dataCount; checkData(); }); auto deletedSubjectsRequest = getSubjectsDeleted(timestamp); // connect(deletedSubjectsRequest.get(), &serversdk::Request::finished, errorHandler); connect(deletedSubjectsRequest.get(), &serversdk::Request::finished, [=, &dataCount](){ qDebug(colnod) << "tokens finished"; saveDeletedSubjects(data, deletedSubjectsRequest->responseData()); ++dataCount; checkData(); // emit partOfDataReady(data); }); }); } void ColnodAPI::processData(const std::shared_ptr &data) { for (const auto &token : data->updatedTokens) { qInfo(colnod).noquote() << Message::massageTemplate("Updating", "token", "colnod", token->name()); } for (const auto &id : data->deletedTokens) { qInfo(colnod).noquote() << Message::massageTemplate("Deleting", "token", "colnod", id); } if (!data->updatedTokens.isEmpty() || !data->deletedTokens.isEmpty()) { qInfo(colnod) << dash; } qInfo(colnod) << tab << "-> DATA PROCESSED TO COLNOD:"; qInfo(colnod) << tab << tab << data->updatedTokens.size() << "TOKENS UPDATED"; qInfo(colnod) << tab << tab << data->deletedTokens.size() << "TOKENS DELETED"; qInfo(colnod) << dash; connect(this, &ColnodAPI::partOfDataProcessFinished, this, &ColnodAPI::isDataProcessFinished); if ( !data->updatedTokens.isEmpty() && !mDryMode) { setTokens(data->updatedTokens); } else { emit partOfDataProcessFinished(); } if ( !data->deletedTokens.isEmpty() && !mDryMode) { deleteTokens(data->deletedTokens); } else { emit partOfDataProcessFinished(); } } std::shared_ptr ColnodAPI::requestLogin() { Q_ASSERT(mReqMan); LoginResp login; login.setUsername(userName()); // password QString encodedPassword = QCryptographicHash::hash( password().toLocal8Bit(), // -> bytearray QCryptographicHash::Sha512 // SHA 512 ).toHex().toUpper(); login.setPassword(encodedPassword); auto loginRequest = composeRequest("/AaaManager/authSession", login.jsonData()); loginRequest->setUserName(login.username()); loginRequest->setPassword(login.password()); mReqMan->processRequest(loginRequest); return loginRequest; } std::shared_ptr ColnodAPI::getTokensUpdated(const Timestamp &time) { auto request = composeRequest("/TokenManager/getTokensUpdated", time.jsonData()); request->setUserName(authToken); mReqMan->processRequest(request); return request; } std::shared_ptr ColnodAPI::getTokensDeleted(const Timestamp &time) { auto request = composeRequest("/TokenManager/getTokensDeleted", time.jsonData()); request->setUserName(authToken); mReqMan->processRequest(request); return request; } std::shared_ptr ColnodAPI::getSubjectsUpdated(const Timestamp &time) { auto request = composeRequest("/SubjectManager/getSubjectsUpdated", time.jsonData()); request->setUserName(authToken); mReqMan->processRequest(request); return request; } std::shared_ptr ColnodAPI::getSubjectsDeleted(const Timestamp &time) { auto request = composeRequest("/SubjectManager/getSubjectsDeleted", time.jsonData()); request->setUserName(authToken); mReqMan->processRequest(request); return request; } std::shared_ptr ColnodAPI::setTokens(const QHash> &tokens) { auto request = composeRequest("/TokenManager/setTokens", Token::tokensToColnodJson(tokens)); request->setUserName(authToken); mReqMan->processRequest(request); connect(request.get(), &serversdk::Request::finished, [=](){ if (!checkRequest(request)) { qCritical(colnod) << "ERROR during calling /TokenManager/setTokens, request unsuccessful. EXIT"; emit error(); } if (checkResponse(request->responseData())) { emit partOfDataProcessFinished(); } else { qWarning(colnod) << "Tokens wasn't updated"; mValid = false; emit partOfDataProcessFinished(); } }); return request; } std::shared_ptr ColnodAPI::deleteTokens(const QList &ids) { auto request = composeRequest("/TokenManager/deleteTokens", Token::idsToJSON(ids)); request->setUserName(authToken); mReqMan->processRequest(request); connect(request.get(), &serversdk::Request::finished, [=](){ if (!checkRequest(request)) { qCritical(colnod) << "ERROR during calling /TokenManager/deleteTokens, request unsuccessful. EXIT"; emit error(); } if (checkResponse(request->responseData())) { emit partOfDataProcessFinished(); } else { qWarning(colnod) << "Tokens wasn't deleted"; mValid = false; emit partOfDataProcessFinished(); } }); return request; } std::shared_ptr ColnodAPI::composeRequest(const QString &endpoint, const QByteArray &data) { QString url = mHost + endpoint; return std::make_shared("POST", url, data); } bool ColnodAPI::checkRequest(const std::shared_ptr &request) { return request->status() == 200; } bool ColnodAPI::isValid() { return mValid; } QString ColnodAPI::password() const { return mPassword; } void ColnodAPI::setPassword(const QString &password) { mPassword = password; } qint64 ColnodAPI::lastSync() const { return mLastSync.timestamp(); } void ColnodAPI::setlastSync(const qint64 &lastSync) { mLastSync.setTimestamp(lastSync); } QString ColnodAPI::userName() const { return mUserName; } void ColnodAPI::setUserName(const QString &userName) { mUserName = userName; } void ColnodAPI::areDataReady(const std::shared_ptr &data) { if (++amountData == 4) { qInfo(colnod) << tab << "-> COLNOD DATA:"; qInfo(colnod) << tab << tab << data->updatedTokens.size() << "TOKENS WERE ADDED/UPDATED"; qInfo(colnod) << tab << tab << data->deletedTokens.size() << "TOKENS DELETED"; qInfo(colnod) << tab << tab << data->updatedSubjects.size() << "SUBJECTS WERE ADDED/UPDATED"; qInfo(colnod) << tab << tab << data->deletedSubjects.size() << "SUBJECTS DELETED"; qInfo(colnod) << dash; amountData = 0; emit downloadDataFinished(data); } } void ColnodAPI::isDataProcessFinished() { if (++amountProccessesFinished == 2) { emit dataProcessFinished(); } } bool ColnodAPI::saveUpdatedTokens(const std::shared_ptr& data, const QByteArray &responseData) { Q_ASSERT(data); return data->loadTokensFromJson(responseData); } bool ColnodAPI::saveDeletedTokens(const std::shared_ptr& data, const QByteArray &responseData) { Q_ASSERT(data); if (responseData.isEmpty()) return false; QJsonDocument doc = QJsonDocument::fromJson(responseData); QJsonArray items = doc.object()["items"].toArray(); for (QJsonValueRef item : items) { data->deletedTokens.append(item.toString()); } return true; } bool ColnodAPI::saveUpdatedSubjects(const std::shared_ptr& data, const QByteArray &responseData) { Q_ASSERT(data); return data->loadSubjectsFormJson(responseData); } bool ColnodAPI::saveDeletedSubjects(const std::shared_ptr& data, const QByteArray &responseData) { Q_ASSERT(data); if (responseData.isEmpty()) return false; QJsonDocument doc = QJsonDocument::fromJson(responseData); QJsonArray items = doc.object()["items"].toArray(); for (QJsonValueRef item : items) { data->deletedSubjects.append(item.toString()); } return true; } void ColnodAPI::newData() { mData = std::make_shared(); } bool ColnodAPI::checkResponse(const QByteArray &responseData) { QJsonDocument doc = QJsonDocument::fromJson(responseData); return doc.object().contains("items"); }