#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 */ bool ColnodAPI::downloadData(const qint64 &time) { // Have to get Login auto login = requestLogin(); Q_ASSERT(login); if (login->networkReply() == nullptr) { qCCritical(colnod) << "Not possible to create login request"; return false; } connect(login.get(), &serversdk::Request::finished, this, [=](){ // Get login token and check if is correct if (!checkRequest(login)) { emit downloadDataFinished(nullptr); return false; } AuthToken token; if (checkResponse(login->responseData())) { qCWarning(colnod) << "empty response data when logging in"; emit downloadDataFinished(nullptr); return false; } token.fromJson(login->responseData()); authToken = token.id(); if (authToken.isEmpty()) { qCCritical(colnod) << "Token uuid is empty"; emit downloadDataFinished(nullptr); return false; } auto data = std::make_shared(); Timestamp timestamp; timestamp.setTimestamp(time); // emit signal when with all data when it is ready auto checkData = [=]() { int dataCount = data->incrementCounter(); if ( dataCount > 5) { qCCritical(colnod) << "check data cannnot be higher then 4"; } Q_ASSERT(dataCount < 5); if (dataCount == 4) { qInfo(colnod) << tab << "- src col:" << tab << "tokens [" << data->updatedTokens.size() << ":" << data->deletedTokens.size() << "] subjects [" << data->updatedSubjects.size() << ":" << data->deletedSubjects.size() << "] [ added/updated : deleted ]"; if (!data->updatedTokens.isEmpty() || !data->deletedTokens.isEmpty() || !data->updatedSubjects.isEmpty() || !data->deletedSubjects.isEmpty()) { qInfo(colnod) << dash; } for (const auto &token : data->updatedTokens) { qInfo(colnod) << Message::massageTemplate("Download updated", "token", "colnod", token->name()); } for (const auto &id : data->deletedTokens) { qInfo(colnod) << Message::massageTemplate("Download deleted", "token", "colnod", id); } for (const auto &subject : data->updatedSubjects) { qInfo(colnod) << Message::massageTemplate("Download updated", "subject", "colnod", subject->name()); } for (const auto &id : data->deletedSubjects) { qInfo(colnod) << Message::massageTemplate("Download deleted", "token", "colnod", id); } if (!data->updatedTokens.isEmpty() || !data->deletedTokens.isEmpty() || !data->updatedSubjects.isEmpty() || !data->deletedSubjects.isEmpty()) { qInfo(colnod) << dash; } emit downloadDataFinished(data); } }; auto tokensRequest = getTokensUpdated(timestamp); connect(tokensRequest.get(), &serversdk::Request::finished, [=](){ if (!checkErrors(tokensRequest)) { qCritical(colnod) << "Error during call getTokensUpdated endpoint"; emit downloadDataFinished(nullptr); return; } if (!saveUpdatedTokens(data, tokensRequest->responseData())) { qCritical(colnod) << "Error during saving getTokensUpdated"; emit downloadDataFinished(nullptr); return; } checkData(); }); auto deletedTokensRequest = getTokensDeleted(timestamp); connect(deletedTokensRequest.get(), &serversdk::Request::finished, [=](){ if (!checkErrors(deletedTokensRequest)) { qCritical(colnod) << "Error during call getTokensDeleted endpoint"; emit downloadDataFinished(nullptr); return; } if (!saveDeletedTokens(data, deletedTokensRequest->responseData())) { qCritical(colnod) << "Error during saving getTokensDeleted"; emit downloadDataFinished(nullptr); return; } checkData(); }); auto subjectsRequest = getSubjectsUpdated(timestamp); connect(subjectsRequest.get(), &serversdk::Request::finished, [=](){ if (!checkErrors(subjectsRequest)) { qCritical(colnod) << "Error during call getSubjectsUpdated endpoint"; emit downloadDataFinished(nullptr); return; } if (!saveUpdatedSubjects(data, subjectsRequest->responseData())) { qCritical(colnod) << "Error during saving getSubjectsUpdated"; emit downloadDataFinished(nullptr); return; } checkData(); }); auto deletedSubjectsRequest = getSubjectsDeleted(timestamp); connect(deletedSubjectsRequest.get(), &serversdk::Request::finished, [=](){ if (!checkErrors(deletedSubjectsRequest)) { qCritical(colnod) << "Error during call getSubjectsDeleted endpoint"; emit downloadDataFinished(nullptr); return; } if (!saveDeletedSubjects(data, deletedSubjectsRequest->responseData())) { qCritical(colnod) << "Error during saving getSubjectsDeleted"; emit downloadDataFinished(nullptr); return; } checkData(); }); return true; }); return true; } void ColnodAPI::processData(const std::shared_ptr &data) { // emit signal when finished auto checkData = [=]() { int dataCount = data->incrementCounter(); if ( dataCount > 3) { qCCritical(colnod) << "check data cannnot be higher then 2"; } Q_ASSERT(dataCount < 3); if (dataCount == 2) { qInfo(colnod) << tab << "- push col:" << tab << "tokens [" << data->updatedTokens.size() << ":" << data->deletedTokens.size() << "] [ added/updated : deleted ]"; if (!data->updatedTokens.isEmpty() || !data->deletedTokens.isEmpty() || !data->updatedSubjects.isEmpty() || !data->deletedSubjects.isEmpty()) { qInfo(colnod) << dash; } for (const auto &token : data->updatedTokens) { qInfo(colnod).noquote() << Message::massageTemplate("Updated", "token", "colnod", token->name()); } for (const auto &id : data->deletedTokens) { qInfo(colnod).noquote() << Message::massageTemplate("Deleted", "token", "colnod", id); } if (!data->updatedTokens.isEmpty() || !data->deletedTokens.isEmpty() || !data->updatedSubjects.isEmpty() || !data->deletedSubjects.isEmpty()) { qInfo(colnod) << dash; } emit dataProcessFinished(mValid); } }; if ( !data->updatedTokens.isEmpty() || !data->deletedTokens.isEmpty() ) { qInfo(colnod) << dash; } if ( !data->updatedTokens.isEmpty() && !mDryMode ) { auto setTokensRequest = setTokens(data->updatedTokens); connect(setTokensRequest.get(), &serversdk::Request::finished, [=](){ if (!checkErrors(setTokensRequest)) { data->updatedTokens.clear(); mValid = false; return; } checkData(); }); } else { checkData(); } if ( !data->deletedTokens.isEmpty() && !mDryMode ) { auto deletedTokensRequest = deleteTokens(data->deletedTokens); connect(deletedTokensRequest.get(), &serversdk::Request::finished, [=](){ if (!checkErrors(deletedTokensRequest)) { data->deletedTokens.clear(); mValid = false; return; } checkData(); }); } else { checkData(); } } 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); return request; } std::shared_ptr ColnodAPI::deleteTokens(const QList &ids) { auto request = composeRequest("/TokenManager/deleteTokens", Token::idsToJSON(ids)); request->setUserName(authToken); mReqMan->processRequest(request); 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::checkErrors(const std::shared_ptr &request) { if (!checkRequest(request)) { qCWarning(colnod) << "Status is not 200"; return false; } if (!checkResponse(request->responseData())) { qCWarning(colnod) << "Empty response data"; return false; } return true; } bool ColnodAPI::isValid() { return mValid; } QString ColnodAPI::password() const { return mPassword; } void ColnodAPI::setPassword(const QString &password) { mPassword = password; } QString ColnodAPI::userName() const { return mUserName; } void ColnodAPI::setUserName(const QString &userName) { mUserName = userName; } 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; } bool ColnodAPI::checkResponse(const QByteArray &responseData) { QJsonDocument doc = QJsonDocument::fromJson(responseData); return doc.object().contains("items"); }