Files
BeetRound/libs/BeetRoundCore/network/servercommunicator.cpp

322 lines
11 KiB
C++

#include "servercommunicator.h"
#include <QJsonArray>
#include <QJsonObject>
#include <QRestAccessManager>
#include <QRestReply>
#include "../formats/jsonparser.h"
#include "../genericcore.h"
#include "../structs.h"
#include "apiroutes.h"
#include "../data/settingshandler.h"
using namespace Qt::StringLiterals;
ServerCommunicator::ServerCommunicator(GenericCore* core)
: m_core(core)
, QObject{core} {
m_netManager.setAutoDeleteReplies(true);
m_restManager = std::make_shared<QRestAccessManager>(&m_netManager);
m_serviceApi = std::make_shared<QNetworkRequestFactory>();
connect(core, &GenericCore::loginAndStoreAuthToken, this, &ServerCommunicator::onLoginTriggered);
connect(this, &ServerCommunicator::loginSuccessful, core, &GenericCore::onLoginSuccessful);
connect(m_core, &GenericCore::sendGetRequest, this,
&ServerCommunicator::onSendGetRequestTriggered);
connect(m_core, &GenericCore::sendPostRequest, this,
&ServerCommunicator::onSendPostRequestTriggered);
connect(this, &ServerCommunicator::biddingsChanged, m_core, &GenericCore::onBiddingsChanged);
connect(this, &ServerCommunicator::onlineUserAccountReceived, m_core,
&GenericCore::onOnlineUserAccountReceived);
}
bool ServerCommunicator::sslSupported() const {
#if QT_CONFIG(ssl)
return QSslSocket::supportsSsl();
#else
return false;
#endif
}
QUrl ServerCommunicator::url() const { return m_serviceApi->baseUrl(); }
void ServerCommunicator::setUrl(const QUrl& url) {
if (m_serviceApi->baseUrl() == url) {
return;
}
m_serviceApi->setBaseUrl(url);
QHttpHeaders authenticationHeaders;
authenticationHeaders.append(QHttpHeaders::WellKnownHeader::ContentType, "application/json");
m_serviceApi->setCommonHeaders(authenticationHeaders);
emit urlChanged();
}
void ServerCommunicator::setServerConfiguration(const QString url,
const QString email,
const QString password,
const QString authToken) {
setUrl(url);
m_email = email;
m_password = password;
m_authToken = authToken;
if (!m_authToken.isEmpty()) {
m_serviceApi->setBearerToken(authToken.toLatin1());
}
}
QString ServerCommunicator::getUserLoginUrl() const {
const QString logInUrl = m_serviceApi->baseUrl().toString() + ROUTE_USER_LOG_IN;
return logInUrl;
}
void ServerCommunicator::onSendGetRequestTriggered(const GetRequestTypes type,
QVariant data = QVariant()) {
QString path;
switch (type) {
case GetCurrentBiddingRound:
path = ROUTE_CURRENT_BIDDINGROUND;
break;
case StartNewBiddingRound:
path = ROUTE_START_BIDDINGROUND;
break;
case RestartLastBiddingRound:
path = ROUTE_RESTART_BIDDINGROUND;
break;
case StopCurrentBiddingRound:
path = ROUTE_STOP_BIDDINGROUND;
break;
case GetBiddingsOfSpecificRound:
path = ROUTE_GET_BIDDINGS_OF_SPECIFIC_ROUND + "/" + data.toString();
break;
case GetBiddingsOfHighestRound:
path = ROUTE_GET_BIDDINGS_OF_HIGHEST_ROUND;
break;
default:
qWarning() << "No route found for GetRequestType:" << type;
break;
}
// TODO move into default case of switch statement?
if (path.isEmpty()) {
qDebug() << "Empty path -> Not sending a request.";
return;
}
m_serviceApi->setBearerToken(m_authToken.toLatin1());
const QNetworkRequest request = m_serviceApi->createRequest(path);
m_restManager->get(request, this, [this, type](QRestReply& reply) {
if (reply.isSuccess()) {
qInfo() << "Request successful.";
const QJsonDocument doc = reply.readJson().value();
onGetReplySuccessful(type, doc);
} else {
if (reply.hasError()) {
const QString errorString = reply.errorString();
qWarning() << "Network error:" << errorString;
onGetReplyFailure(type, errorString);
} else {
int statusCode = reply.httpStatus();
qWarning() << "Request not successful:" << statusCode;
onGetReplyFailure(type, QString("HTTP status code: %1").arg(statusCode));
}
}
});
}
void ServerCommunicator::onGetReplySuccessful(const GetRequestTypes type, const QJsonDocument doc) {
switch (type) {
case GetCurrentBiddingRound:
case StartNewBiddingRound:
case RestartLastBiddingRound:
case StopCurrentBiddingRound:
currentBiddingRoundChangedReply(doc);
break;
case GetBiddingsOfSpecificRound:
case GetBiddingsOfHighestRound:
currentBiddingsReply(doc);
break;
default:
qWarning() << "Can't match request type:" << type;
break;
}
}
void ServerCommunicator::onGetReplyFailure(const GetRequestTypes type, const QString errorString) {
const QString message =
QString("Request of type %1 returned: %2").arg(QString::number(type), errorString);
m_core->displayStatusMessage(message);
}
void ServerCommunicator::onSendPostRequestTriggered(const PostRequestTypes type,
const QByteArray data) {
QString path;
switch (type) {
case LogInAdmin:
path = ROUTE_ADMIN_LOG_IN;
break;
case RegisterUser:
path = ROUTE_REGISTER_USER;
break;
case MailInvite:
path = ROUTE_MAIL_INVITE;
break;
default:
qWarning() << "No route found for PostRequestType:" << type;
break;
}
// TODO move into default case of switch statement?
if (path.isEmpty()) {
qDebug() << "Empty path -> Not sending a request.";
return;
}
m_serviceApi->setBearerToken(m_authToken.toLatin1());
const QNetworkRequest request = m_serviceApi->createRequest(path);
m_restManager->post(request, data, this, [this, type](QRestReply& reply) {
if (reply.isSuccess()) {
int statusCode = reply.httpStatus();
qInfo() << "Request successful. Status code:" << statusCode;
const QJsonDocument doc = reply.readJson().value();
onPostReplySuccessful(type, doc);
} else {
if (reply.hasError()) {
const QString errorString = reply.errorString();
qWarning() << "Network error:" << errorString;
onPostReplyFailure(type, errorString);
} else {
int statusCode = reply.httpStatus();
qWarning() << "Request not successful:" << statusCode;
qCritical() << "Content:" << reply.readJson();
onPostReplyFailure(type, QString("HTTP status code: %1").arg(statusCode));
}
}
});
}
void ServerCommunicator::onPostReplySuccessful(const PostRequestTypes type,
const QJsonDocument doc) {
switch (type) {
case LogInAdmin:
qInfo() << "Admin successfully logged in:" << type;
handleLogInReply(doc);
break;
case RegisterUser:
qInfo() << "Register user successful:" << type;
onlineUserAccountReply(doc);
break;
case MailInvite:
qInfo() << "Mail invite successful sent:" << type;
mailInviteSentReply(doc);
break;
default:
qWarning() << "Can't match request type:" << type;
break;
}
}
void ServerCommunicator::onPostReplyFailure(const PostRequestTypes type,
const QString errorString) {
const QString message =
QString("Request of type %1 returned: %2").arg(QString::number(type), errorString);
// NEXT improve error message to the UI;
emit m_core->displayStatusMessage(message);
}
QByteArray ServerCommunicator::createLoginBody() {
QHash<QString, QVariant> values;
values.insert("email", m_email);
values.insert("password", m_password);
return JsonParser::toJsonDoc(values, "admin");
}
void ServerCommunicator::handleLogInReply(const QJsonDocument jsonDoc) {
QJsonObject rootObject = jsonDoc.object();
bool hasErrors = rootObject.contains(QString("errors"));
if (hasErrors) {
qCritical() << "Reply has error(s)!";
QString errorString = rootObject["errors"].toString();
emit m_core->displayStatusMessage(errorString);
} else {
qInfo() << "Reply has no error(s)!";
const QJsonObject dataObject = rootObject["data"].toObject();
const QString authToken = dataObject["token"].toString();
m_authToken = authToken;
SettingsHandler::saveSettings({{"token", authToken}}, "Server");
emit loginSuccessful();
emit m_core->displayStatusMessage("Successfully logged in.");
}
}
void ServerCommunicator::currentBiddingRoundChangedReply(const QJsonDocument jsonDoc) {
qInfo() << "Current bidding round received.";
// REFACTOR implement & use "JsonParser::parseServerResponse(const QJsonDocument& jsonDoc,
// QHash<QString, int> entries)" (generalized version of
// "serverUserCredentialsToItemValues")
const QJsonObject rootObject = jsonDoc["data"].toObject();
const int roundNumber = rootObject["round_number"].toInt();
const bool stopped = rootObject["stopped"].toBool();
const bool isActive = !stopped;
// NOTE ?use ServerCommunicator::currentBiddingRoundChanged signal instead of emiting a signal of
// the core directly?
emit m_core->currentBiddingRoundChanged(roundNumber, isActive);
}
void ServerCommunicator::currentBiddingsReply(const QJsonDocument jsonDoc) {
qCritical() << "currentBiddingsReply:" << jsonDoc;
const QList<bidding> biddings = JsonParser::extractBiddings(jsonDoc);
emit biddingsChanged(biddings);
}
void ServerCommunicator::onlineUserAccountReply(const QJsonDocument jsonDoc) {
qInfo() << "Online user account received.";
ModelItemValues values = JsonParser::serverUserCredentialsToItemValues(jsonDoc);
emit onlineUserAccountReceived(values[MailRole].toString(), values[OnlineIdRole].toString(),
values[AccessCodeRole].toString());
}
void ServerCommunicator::mailInviteSentReply(const QJsonDocument jsonDoc) {
qInfo() << "Invitation mail successfully sent.";
emit m_core->displayStatusMessage("Invitation mail successfully sent.");
}
void ServerCommunicator::onLoginTriggered() {
qInfo() << "Login triggered...";
if (m_email.isEmpty() || m_password.isEmpty()) {
emit m_core->displayStatusMessage(
"Missing email or password in settings! Not trying to log in.");
return;
}
if (m_authToken.isEmpty()) {
/// get new authToken
qWarning() << "Creating a new authToken!";
const QByteArray loginBody = createLoginBody();
onSendPostRequestTriggered(LogInAdmin, loginBody);
} else {
/// try authToken
qWarning() << "Validity check of token not implemented yet!!!";
qInfo() << "Assuming validity of token...";
emit loginSuccessful();
// TODO try validity of token and trigger log_in if not valid
// try default route to test access;
// if (access denied) {
// m_apiClient->sendAPIRequestPost(ROUTE_LOG_IN_ADMIN, );
// }
}
}