#include "servercommunicator.h" #include #include #include #include #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(&m_netManager); m_serviceApi = std::make_shared(); 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 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 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 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, ); // } } }