colnod-connector/src/colnod/colnodapi.cpp
2019-12-16 09:18:06 +01:00

385 lines
13 KiB
C++

#include "colnodapi.h"
#include <utility>
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<Data>();
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> &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<serversdk::Request> 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<serversdk::Request> ColnodAPI::getTokensUpdated(const Timestamp &time)
{
auto request = composeRequest("/TokenManager/getTokensUpdated", time.jsonData());
request->setUserName(authToken);
mReqMan->processRequest(request);
return request;
}
std::shared_ptr<serversdk::Request> ColnodAPI::getTokensDeleted(const Timestamp &time)
{
auto request = composeRequest("/TokenManager/getTokensDeleted", time.jsonData());
request->setUserName(authToken);
mReqMan->processRequest(request);
return request;
}
std::shared_ptr<serversdk::Request> ColnodAPI::getSubjectsUpdated(const Timestamp &time)
{
auto request = composeRequest("/SubjectManager/getSubjectsUpdated", time.jsonData());
request->setUserName(authToken);
mReqMan->processRequest(request);
return request;
}
std::shared_ptr<serversdk::Request> ColnodAPI::getSubjectsDeleted(const Timestamp &time)
{
auto request = composeRequest("/SubjectManager/getSubjectsDeleted", time.jsonData());
request->setUserName(authToken);
mReqMan->processRequest(request);
return request;
}
std::shared_ptr<serversdk::Request> ColnodAPI::setTokens(const QHash<QString, std::shared_ptr<Token>> &tokens)
{
auto request = composeRequest("/TokenManager/setTokens", Token::tokensToColnodJson(tokens));
request->setUserName(authToken);
mReqMan->processRequest(request);
return request;
}
std::shared_ptr<serversdk::Request> ColnodAPI::deleteTokens(const QList<QString> &ids)
{
auto request = composeRequest("/TokenManager/deleteTokens", Token::idsToJSON(ids));
request->setUserName(authToken);
mReqMan->processRequest(request);
return request;
}
std::shared_ptr<serversdk::Request> ColnodAPI::composeRequest(const QString &endpoint, const QByteArray &data)
{
QString url = mHost + endpoint;
return std::make_shared<serversdk::Request>("POST", url, data);
}
bool ColnodAPI::checkRequest(const std::shared_ptr<serversdk::Request> &request)
{
return request->status() == 200;
}
bool ColnodAPI::checkErrors(const std::shared_ptr<serversdk::Request> &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>& data, const QByteArray &responseData)
{
Q_ASSERT(data);
return data->loadTokensFromJson(responseData);
}
bool ColnodAPI::saveDeletedTokens(const std::shared_ptr<Data>& 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>& data, const QByteArray &responseData)
{
Q_ASSERT(data);
return data->loadSubjectsFormJson(responseData);
}
bool ColnodAPI::saveDeletedSubjects(const std::shared_ptr<Data>& 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");
}