File indexing completed on 2024-05-05 17:00:21

0001 /*
0002    SPDX-FileCopyrightText: 2018-2024 Laurent Montel <montel@kde.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "restapiabstractjob.h"
0008 #include "abstractlogger.h"
0009 #include "rocketchatqtrestapi_debug.h"
0010 #include <KLocalizedString>
0011 #include <QJsonDocument>
0012 #include <QJsonObject>
0013 #include <QUrlQuery>
0014 
0015 using namespace RocketChatRestApi;
0016 RestApiAbstractJob::RestApiAbstractJob(QObject *parent)
0017     : QObject(parent)
0018 {
0019 }
0020 
0021 RestApiAbstractJob::~RestApiAbstractJob()
0022 {
0023     if (mReply) {
0024         mReply->disconnect(this);
0025     }
0026 }
0027 
0028 QNetworkAccessManager *RestApiAbstractJob::networkAccessManager() const
0029 {
0030     return mNetworkAccessManager;
0031 }
0032 
0033 void RestApiAbstractJob::setNetworkAccessManager(QNetworkAccessManager *networkAccessManager)
0034 {
0035     mNetworkAccessManager = networkAccessManager;
0036 }
0037 
0038 RocketChatRestApi::RestApiMethod *RestApiAbstractJob::restApiMethod() const
0039 {
0040     return mRestApiMethod;
0041 }
0042 
0043 void RestApiAbstractJob::setRestApiMethod(RocketChatRestApi::RestApiMethod *restApiMethod)
0044 {
0045     mRestApiMethod = restApiMethod;
0046 }
0047 
0048 QString RestApiAbstractJob::authToken() const
0049 {
0050     return mAuthToken;
0051 }
0052 
0053 void RestApiAbstractJob::setAuthToken(const QString &authToken)
0054 {
0055     mAuthToken = authToken;
0056 }
0057 
0058 QString RestApiAbstractJob::userId() const
0059 {
0060     return mUserId;
0061 }
0062 
0063 void RestApiAbstractJob::setUserId(const QString &userId)
0064 {
0065     mUserId = userId;
0066 }
0067 
0068 bool RestApiAbstractJob::hasAuthenticationValue() const
0069 {
0070     return !mAuthToken.isEmpty() && !mUserId.isEmpty();
0071 }
0072 
0073 bool RestApiAbstractJob::requireTwoFactorAuthentication() const
0074 {
0075     return false;
0076 }
0077 
0078 bool RestApiAbstractJob::hasQueryParameterSupport() const
0079 {
0080     return false;
0081 }
0082 
0083 bool RestApiAbstractJob::enforcePasswordFallback() const
0084 {
0085     return mEnforcePasswordFallBack;
0086 }
0087 
0088 void RestApiAbstractJob::setEnforcePasswordFallback(bool enforce)
0089 {
0090     mEnforcePasswordFallBack = enforce;
0091 }
0092 
0093 bool RestApiAbstractJob::canStart() const
0094 {
0095     if (requireTwoFactorAuthentication() && mEnforcePasswordFallBack) {
0096         if (mAuthMethod.isEmpty() || mAuthCode.isEmpty()) {
0097             qCWarning(ROCKETCHATQTRESTAPI_LOG) << "Job required two factor auth but mAuthMethod or mAuthCode is empty";
0098             return false;
0099         }
0100     }
0101     if (!mNetworkAccessManager) {
0102         qCWarning(ROCKETCHATQTRESTAPI_LOG) << "Network manager not defined";
0103         return false;
0104     }
0105     if (!mRestApiMethod) {
0106         qCWarning(ROCKETCHATQTRESTAPI_LOG) << "RestApiMethod not defined";
0107         return false;
0108     }
0109     if (requireHttpAuthentication() && !hasAuthenticationValue()) {
0110         qCWarning(ROCKETCHATQTRESTAPI_LOG) << "Auth settings is empty. It's a bug";
0111         return false;
0112     }
0113     return true;
0114 }
0115 
0116 void RestApiAbstractJob::addRequestAttribute(QNetworkRequest &request, bool addContentTypeHeader) const
0117 {
0118     request.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
0119     request.setAttribute(QNetworkRequest::Http2AllowedAttribute, true);
0120     if (addContentTypeHeader) {
0121         request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json"));
0122     }
0123 }
0124 
0125 void RestApiAbstractJob::addAuthRawHeader(QNetworkRequest &request) const
0126 {
0127     request.setRawHeader(QByteArrayLiteral("X-Auth-Token"), mAuthToken.toLocal8Bit());
0128     request.setRawHeader(QByteArrayLiteral("X-User-Id"), mUserId.toLocal8Bit());
0129     if (requireTwoFactorAuthentication() && mEnforcePasswordFallBack) {
0130         if (!mAuthMethod.isEmpty() && !mAuthCode.isEmpty()) {
0131             request.setRawHeader(QByteArrayLiteral("x-2fa-code"), mAuthCode.toLocal8Bit());
0132             request.setRawHeader(QByteArrayLiteral("x-2fa-method"), mAuthMethod.toLocal8Bit());
0133         }
0134     }
0135 }
0136 
0137 QueryParameters RestApiAbstractJob::queryParameters() const
0138 {
0139     return mQueryParameters;
0140 }
0141 
0142 void RestApiAbstractJob::setQueryParameters(const QueryParameters &queryParameters)
0143 {
0144     mQueryParameters = queryParameters;
0145 }
0146 
0147 void RestApiAbstractJob::addQueryParameter(QUrlQuery &urlQuery) const
0148 {
0149     if (hasQueryParameterSupport() && mQueryParameters.isValid()) {
0150         QueryParameters::generateQueryParameter(mQueryParameters, urlQuery);
0151     }
0152     // qDebug() << " urlQuery " << urlQuery.toString();
0153 }
0154 
0155 RocketChatRestApi::AbstractLogger *RestApiAbstractJob::restApiLogger() const
0156 {
0157     return mRestApiLogger;
0158 }
0159 
0160 void RestApiAbstractJob::setRestApiLogger(RocketChatRestApi::AbstractLogger *ruqolaLogger)
0161 {
0162     mRestApiLogger = ruqolaLogger;
0163 }
0164 
0165 void RestApiAbstractJob::addLoggerInfo(const QByteArray &str)
0166 {
0167     if (mRestApiLogger) { // when $RUQOLA_LOGFILE is set
0168         mRestApiLogger->dataSent("RESTAPI: " + str);
0169     } else {
0170         qCDebug(ROCKETCHATQTRESTAPI_LOG) << "RESTAPI: " << str;
0171     }
0172 }
0173 
0174 void RestApiAbstractJob::addStartRestApiInfo(const QByteArray &str)
0175 {
0176     if (mRestApiLogger) { // when $RUQOLA_LOGFILE is set
0177         mRestApiLogger->dataSent(AbstractLogger::RESTApiType, "RESTAPI:", str);
0178     } else {
0179         qCDebug(ROCKETCHATQTRESTAPI_LOG) << "RESTAPI: " << str;
0180     }
0181 }
0182 
0183 void RestApiAbstractJob::addLoggerWarning(const QByteArray &str)
0184 {
0185     if (mRestApiLogger) {
0186         mRestApiLogger->dataSent(AbstractLogger::RESTApiType, "WARNING RESTAPI: ", str);
0187     } else {
0188         qCWarning(ROCKETCHATQTRESTAPI_LOG) << "RESTAPI: " << str;
0189     }
0190 }
0191 
0192 void RestApiAbstractJob::emitFailedMessage(const QString &replyErrorString, const QJsonObject &replyObject)
0193 {
0194     Q_EMIT failed(replyErrorString + QLatin1Char('\n') + errorStr(replyObject));
0195 }
0196 
0197 QString RestApiAbstractJob::errorStr(const QJsonObject &replyObject)
0198 {
0199     // JSon-level error
0200     const QString errorType = replyObject[QLatin1String("errorType")].toString();
0201     if (!errorType.isEmpty()) {
0202         qCWarning(ROCKETCHATQTRESTAPI_LOG) << "errorType" << errorType;
0203         const QString trStr = errorMessage(errorType, replyObject[QLatin1String("details")].toObject());
0204         if (!trStr.isEmpty()) {
0205             return trStr;
0206         } else {
0207             qCWarning(ROCKETCHATQTRESTAPI_LOG) << " errorType not defined as translated message: " << errorType;
0208             return i18n("Unauthorized");
0209         }
0210     } else if (replyObject[QLatin1String("status")].toString() == QLatin1String("error")) {
0211         const QString message = replyObject[QLatin1String("message")].toString();
0212         qCWarning(ROCKETCHATQTRESTAPI_LOG) << "message error " << message;
0213         return generateErrorMessage(message);
0214     } else {
0215         const QString error = replyObject[QLatin1String("error")].toString();
0216         qCWarning(ROCKETCHATQTRESTAPI_LOG) << "error " << error;
0217         return generateErrorMessage(error);
0218     }
0219 }
0220 
0221 QString RestApiAbstractJob::generateErrorMessage(const QString &errorStr) const
0222 {
0223     if (jobName().isEmpty()) {
0224         return errorStr;
0225     }
0226     return i18n("%1:%2", jobName(), errorStr);
0227 }
0228 
0229 QString RestApiAbstractJob::errorMessage(const QString &str, const QJsonObject &details)
0230 {
0231     qDebug() << " details " << details;
0232     if (str == QLatin1String("error-action-not-allowed")) {
0233         const QString actionName = details[QLatin1String("action")].toString();
0234         return i18n("'%1' is not allowed", actionName);
0235     } else if (str == QLatin1String("error-application-not-found")) {
0236         return i18n("Application not found");
0237     } else if (str == QLatin1String("error-archived-duplicate-name")) {
0238         const QString roomName = details[QLatin1String("room_name")].toString();
0239         return i18n("There's an archived channel with name '%1'", roomName);
0240     } else if (str == QLatin1String("error-cant-invite-for-direct-room")) {
0241         return i18n("Can't invite user to direct rooms");
0242     } else if (str == QLatin1String("error-channels-setdefault-is-same")) {
0243         return i18n("The channel default setting is the same as what it would be changed to.");
0244     } else if (str == QLatin1String("error-channels-setdefault-missing-default-param")) {
0245         return i18n("The bodyParam 'default' is required");
0246     } else if (str == QLatin1String("error-could-not-change-email")) {
0247         return i18n("Could not change email");
0248     } else if (str == QLatin1String("error-could-not-change-name")) {
0249         return i18n("Could not change name");
0250     } else if (str == QLatin1String("error-could-not-change-username")) {
0251         return i18n("Could not change username");
0252     } else if (str == QLatin1String("error-delete-protected-role")) {
0253         return i18n("Cannot delete a protected role");
0254     } else if (str == QLatin1String("error-department-not-found")) {
0255         return i18n("Department not found");
0256     } else if (str == QLatin1String("error-direct-message-file-upload-not-allowed")) {
0257         return i18n("File sharing not allowed in direct messages");
0258     } else if (str == QLatin1String("error-duplicate-channel-name")) {
0259         const QString channelName = details[QLatin1String("channel_name")].toString();
0260         return i18n("A channel with name '%1' exists", channelName);
0261     } else if (str == QLatin1String("error-edit-permissions-not-allowed")) {
0262         return i18n("Editing permissions is not allowed");
0263     } else if (str == QLatin1String("error-email-domain-blacklisted")) {
0264         return i18n("The email domain is blacklisted");
0265     } else if (str == QLatin1String("error-email-send-failed")) {
0266         const QString message = details[QLatin1String("message")].toString();
0267         return i18n("Error trying to send email: %1", message);
0268     } else if (str == QLatin1String("error-field-unavailable")) {
0269         const QString field = details[QLatin1String("field")].toString();
0270         return i18n("'%1' is already in use :(", field);
0271     } else if (str == QLatin1String("error-file-too-large")) {
0272         return i18n("File is too large");
0273     } else if (str == QLatin1String("error-importer-not-defined")) {
0274         return i18n("The importer was not defined correctly, it is missing the Import class.");
0275     } else if (str == QLatin1String("error-import-file-extract-error")) {
0276         return i18n("Failed to extract import file.");
0277     } else if (str == QLatin1String("error-import-file-is-empty")) {
0278         return i18n("Imported file seems to be empty.");
0279     } else if (str == QLatin1String("error-import-file-missing")) {
0280         return i18n("The file to be imported was not found on the specified path.");
0281     } else if (str == QLatin1String("error-input-is-not-a-valid-field")) {
0282         const QString field = details[QLatin1String("field")].toString();
0283         const QString input = details[QLatin1String("input")].toString();
0284         return i18n("%1 is not a valid %2", input, field);
0285     } else if (str == QLatin1String("error-invalid-actionlink")) {
0286         return i18n("Invalid action link");
0287     } else if (str == QLatin1String("error-invalid-account")) {
0288         return i18n("Invalid Account");
0289     } else if (str == QLatin1String("error-invalid-arguments")) {
0290         return i18n("Invalid arguments");
0291     } else if (str == QLatin1String("error-invalid-asset")) {
0292         return i18n("Invalid asset");
0293     } else if (str == QLatin1String("error-invalid-channel")) {
0294         return i18n("Invalid channel.");
0295     } else if (str == QLatin1String("error-invalid-channel-start-with-chars")) {
0296         return i18n("Invalid channel. Start with @ or #");
0297     } else if (str == QLatin1String("error-invalid-custom-field")) {
0298         return i18n("Invalid custom field");
0299     } else if (str == QLatin1String("error-invalid-custom-field-name")) {
0300         return i18n("Invalid custom field name. Use only letters, numbers, hyphens and underscores.");
0301     } else if (str == QLatin1String("error-invalid-date")) {
0302         return i18n("Invalid date provided.");
0303     } else if (str == QLatin1String("error-invalid-description")) {
0304         return i18n("Invalid description");
0305     } else if (str == QLatin1String("error-invalid-domain")) {
0306         return i18n("Invalid domain");
0307     } else if (str == QLatin1String("error-invalid-email")) {
0308         const QString email = details[QLatin1String("email")].toString();
0309         return i18n("Invalid email '%1'", email);
0310     } else if (str == QLatin1String("error-invalid-email-address")) {
0311         return i18n("Invalid email address");
0312     } else if (str == QLatin1String("error-invalid-file-height")) {
0313         return i18n("Invalid file height");
0314     } else if (str == QLatin1String("error-invalid-file-type")) {
0315         return i18n("Invalid file type");
0316     } else if (str == QLatin1String("error-invalid-file-width")) {
0317         return i18n("Invalid file width");
0318     } else if (str == QLatin1String("error-invalid-from-address")) {
0319         return i18n("You informed an invalid FROM address.");
0320     } else if (str == QLatin1String("error-invalid-integration")) {
0321         return i18n("Invalid integration");
0322     } else if (str == QLatin1String("error-invalid-message")) {
0323         return i18n("Invalid message");
0324     } else if (str == QLatin1String("error-invalid-method")) {
0325         return i18n("Invalid method");
0326     } else if (str == QLatin1String("error-invalid-name")) {
0327         return i18n("Invalid name");
0328     } else if (str == QLatin1String("error-invalid-password")) {
0329         return i18n("Invalid password");
0330     } else if (str == QLatin1String("error-invalid-permission")) {
0331         return i18n("Invalid permission");
0332     } else if (str == QLatin1String("error-invalid-redirectUri")) {
0333         return i18n("Invalid redirectUri");
0334     } else if (str == QLatin1String("error-invalid-role")) {
0335         return i18n("Invalid role");
0336     } else if (str == QLatin1String("error-invalid-room")) {
0337         return i18n("Invalid room");
0338     } else if (str == QLatin1String("error-invalid-room-name")) {
0339         const QString roomName = details[QLatin1String("room_name")].toString();
0340         return i18n("'%1' is not a valid room name", roomName);
0341     } else if (str == QLatin1String("error-invalid-room-type")) {
0342         const QString roomType = details[QLatin1String("type")].toString();
0343         return i18n("'%1' is not a valid room type.", roomType);
0344     } else if (str == QLatin1String("error-invalid-settings")) {
0345         return i18n("Invalid settings provided");
0346     } else if (str == QLatin1String("error-invalid-subscription")) {
0347         return i18n("Invalid subscription");
0348     } else if (str == QLatin1String("error-invalid-token")) {
0349         return i18n("Invalid token");
0350     } else if (str == QLatin1String("error-invalid-triggerWords")) {
0351         return i18n("Invalid triggerWords");
0352     } else if (str == QLatin1String("error-invalid-urls")) {
0353         return i18n("Invalid URLs");
0354     } else if (str == QLatin1String("error-invalid-user")) {
0355         return i18n("Invalid user");
0356     } else if (str == QLatin1String("error-invalid-username")) {
0357         return i18n("Invalid username");
0358     } else if (str == QLatin1String("error-invalid-webhook-response")) {
0359         return i18n("The webhook URL responded with a status other than 200");
0360     } else if (str == QLatin1String("error-message-deleting-blocked")) {
0361         return i18n("Message deleting is blocked");
0362     } else if (str == QLatin1String("error-message-editing-blocked")) {
0363         return i18n("Message editing is blocked");
0364     } else if (str == QLatin1String("error-message-size-exceeded")) {
0365         return i18n("Message size exceeds Message_MaxAllowedSize");
0366     } else if (str == QLatin1String("error-missing-unsubscribe-link")) {
0367         return i18n("You must provide the [unsubscribe] link.");
0368     } else if (str == QLatin1String("error-no-tokens-for-this-user")) {
0369         return i18n("There are no tokens for this user");
0370     } else if (str == QLatin1String("error-not-allowed")) {
0371         return i18n("Not allowed");
0372     } else if (str == QLatin1String("error-not-authorized")) {
0373         return i18n("Not authorized");
0374     } else if (str == QLatin1String("not-authorized")) {
0375         return i18n("Not authorized");
0376     } else if (str == QLatin1String("error-password-policy-not-met")) {
0377         return i18n("Password does not meet the server's policy");
0378     } else if (str == QLatin1String("error-password-policy-not-met-maxLength")) {
0379         return i18n("Password does not meet the server's policy of maximum length (password too long)");
0380     } else if (str == QLatin1String("error-password-policy-not-met-minLength")) {
0381         return i18n("Password does not meet the server's policy of minimum length (password too short)");
0382     } else if (str == QLatin1String("error-password-policy-not-met-oneLowercase")) {
0383         return i18n("Password does not meet the server's policy of at least one lowercase character");
0384     } else if (str == QLatin1String("error-password-policy-not-met-oneNumber")) {
0385         return i18n("Password does not meet the server's policy of at least one numerical character");
0386     } else if (str == QLatin1String("error-password-policy-not-met-oneSpecial")) {
0387         return i18n("Password does not meet the server's policy of at least one special character");
0388     } else if (str == QLatin1String("error-password-policy-not-met-oneUppercase")) {
0389         return i18n("Password does not meet the server's policy of at least one uppercase character");
0390     } else if (str == QLatin1String("error-password-policy-not-met-repeatingCharacters")) {
0391         return i18n(
0392             "Password does not meet the server's policy of forbidden repeating characters (you have too many of the same characters next to each other)");
0393     } else if (str == QLatin1String("error-push-disabled")) {
0394         return i18n("Push is disabled");
0395     } else if (str == QLatin1String("error-remove-last-owner")) {
0396         return i18n("This is the last owner. Please set a new owner before removing this one.");
0397     } else if (str == QLatin1String("error-role-in-use")) {
0398         return i18n("Cannot delete role because it's in use");
0399     } else if (str == QLatin1String("error-role-name-required")) {
0400         return i18n("Role name is required");
0401     } else if (str == QLatin1String("error-room-is-not-closed")) {
0402         return i18n("Room is not closed");
0403     } else if (str == QLatin1String("error-the-field-is-required")) {
0404         const QString field = details[QLatin1String("field")].toString();
0405         return i18n("The field '%1' is required.", field);
0406     } else if (str == QLatin1String("error-this-is-not-a-livechat-room")) {
0407         return i18n("This is not a Livechat room");
0408     } else if (str == QLatin1String("error-personal-access-tokens-are-current-disabled")) {
0409         return i18n("Personal Access Tokens are currently disabled");
0410     } else if (str == QLatin1String("error-token-already-exists")) {
0411         return i18n("A token with this name already exists");
0412     } else if (str == QLatin1String("error-token-does-not-exists")) {
0413         return i18n("Token does not exists");
0414     } else if (str == QLatin1String("error-too-many-requests")) {
0415         const QString seconds = details[QLatin1String("seconds")].toString();
0416         return i18n("Error, too many requests. Please slow down. You must wait %1 seconds before trying again.", seconds);
0417     } else if (str == QLatin1String("error-user-has-no-roles")) {
0418         return i18n("User has no roles");
0419     } else if (str == QLatin1String("error-user-is-not-activated")) {
0420         return i18n("User is not activated");
0421     } else if (str == QLatin1String("error-user-limit-exceeded")) {
0422         return i18n("The number of users you are trying to invite to #channel_name exceeds the limit set by the administrator");
0423     } else if (str == QLatin1String("error-user-not-in-room")) {
0424         return i18n("User is not in this room");
0425     } else if (str == QLatin1String("error-logged-user-not-in-room")) {
0426         return i18n("You are not in the room `%s`");
0427     } else if (str == QLatin1String("error-user-registration-disabled")) {
0428         return i18n("User registration is disabled");
0429     } else if (str == QLatin1String("error-user-registration-secret")) {
0430         return i18n("User registration is only allowed via Secret URL");
0431     } else if (str == QLatin1String("error-you-are-last-owner")) {
0432         return i18n("You are the last owner. Please set new owner before leaving the room.");
0433     } else if (str == QLatin1String("error-room-archived")) {
0434         return i18n("The private group is archived");
0435     } else if (str == QLatin1String("error-user-already-owner")) {
0436         return i18n("User is already an owner");
0437     } else if (str == QLatin1String("error-user-already-leader")) {
0438         return i18n("User is already a leader");
0439     } else if (str == QLatin1String("error-user-already-moderator")) {
0440         return i18n("User is already a moderator");
0441     } else if (str == QLatin1String("error-invalid-message_id")) {
0442         return i18n("Invalid message id");
0443     } else if (str == QLatin1String("error-user-not-leader")) {
0444         return i18n("User is not a leader");
0445     } else if (str == QLatin1String("error-app-user-is-not-allowed-to-login")) {
0446         return i18n("App user is not allowed to login");
0447     } else if (str == QLatin1String("error-direct-message-room")) {
0448         return i18n("Direct Messages can not be archived");
0449     } else if (str == QLatin1String("error-message-not-found")) {
0450         return i18n("Message not found.");
0451     } else if (str == QLatin1String("totp-required")) {
0452         return i18n("Two Authentication Password Required");
0453     } else if (str == QLatin1String("totp-invalid")) {
0454         return i18n("Invalid Password");
0455     } else if (str == QLatin1String("error-room-not-found")) {
0456         return i18n("The required \\\"roomId\\\" or \\\"roomName\\\" param provided does not match any channel");
0457     } else if (str == QLatin1String("error-role-already-present")) {
0458         return i18n("A role with this name already exists");
0459     } else if (str == QLatin1String("error-pinning-message")) {
0460         return i18n("Message could not be pinned");
0461     } else if (str == QLatin1String("error-password-in-history")) {
0462         return i18n("Entered password has been previously used");
0463     } else if (str == QLatin1String("error-max-rooms-per-guest-reached")) {
0464         return i18n("The maximum number of rooms per guest has been reached.");
0465     } else {
0466         qCWarning(ROCKETCHATQTRESTAPI_LOG) << " unknown error type " << str;
0467         return {};
0468     }
0469 }
0470 
0471 QString RestApiAbstractJob::jobName() const
0472 {
0473     return {};
0474 }
0475 
0476 void RestApiAbstractJob::genericResponseHandler(void (RestApiAbstractJob::*responseHandler)(const QString &, const QJsonDocument &))
0477 {
0478     if (!mReply) {
0479         deleteLater();
0480         return;
0481     }
0482 
0483     if (mReply->error() != QNetworkReply::NoError) {
0484         if (mReply->error() == QNetworkReply::NetworkSessionFailedError) {
0485             // Ignore NetworkSessionFailedError. It will be handled in Connection class.
0486             // no deleting, we will be trying to destroy everything and relogin
0487             // reply will be invalid at this point, deleting it will crash us
0488             qCWarning(ROCKETCHATQTRESTAPI_LOG) << "NetworkSessionFailedError. Lost connection? ";
0489             return;
0490         }
0491         // TODO add support error 400
0492         (this->*responseHandler)(mReply->errorString(), convertToJsonDocument(mReply));
0493     } else {
0494         (this->*responseHandler)(QString(), convertToJsonDocument(mReply));
0495     }
0496 
0497     mReply->deleteLater();
0498     deleteLater();
0499 }
0500 
0501 QDateTime RestApiAbstractJob::updatedSince() const
0502 {
0503     return mUpdatedSince;
0504 }
0505 
0506 void RestApiAbstractJob::setUpdatedSince(const QDateTime &newUpdatedSince)
0507 {
0508     mUpdatedSince = newUpdatedSince;
0509 }
0510 
0511 const QString &RestApiAbstractJob::authCode() const
0512 {
0513     return mAuthCode;
0514 }
0515 
0516 void RestApiAbstractJob::setAuthCode(const QString &newAuthCode)
0517 {
0518     mAuthCode = newAuthCode;
0519 }
0520 
0521 const QString &RestApiAbstractJob::authMethod() const
0522 {
0523     return mAuthMethod;
0524 }
0525 
0526 void RestApiAbstractJob::setAuthMethod(const QString &newAuthMethod)
0527 {
0528     mAuthMethod = newAuthMethod;
0529 }
0530 
0531 void RestApiAbstractJob::submitDeleteRequest()
0532 {
0533     mReply = mNetworkAccessManager->deleteResource(request());
0534     const QByteArray className = metaObject()->className();
0535     mReply->setProperty("jobClassName", className);
0536 
0537     connect(mReply.data(), &QNetworkReply::finished, this, [this] {
0538         genericResponseHandler(&RestApiAbstractJob::onDeleteRequestResponse);
0539     });
0540 }
0541 
0542 void RestApiAbstractJob::submitGetRequest()
0543 {
0544     mReply = mNetworkAccessManager->get(request());
0545     const QByteArray className = metaObject()->className();
0546     mReply->setProperty("jobClassName", className);
0547 
0548     connect(mReply.data(), &QNetworkReply::finished, this, [this] {
0549         genericResponseHandler(&RestApiAbstractJob::onGetRequestResponse);
0550     });
0551 }
0552 
0553 void RestApiAbstractJob::submitPostRequest(const QJsonDocument &doc)
0554 {
0555     const QByteArray baPostData = doc.isNull() ? QByteArray() : doc.toJson(QJsonDocument::Compact);
0556     mReply = mNetworkAccessManager->post(request(), baPostData);
0557     const QByteArray className = metaObject()->className();
0558     mReply->setProperty("jobClassName", className);
0559 
0560     addLoggerInfo(className + " started " + baPostData);
0561 
0562     connect(mReply.data(), &QNetworkReply::finished, this, [this] {
0563         genericResponseHandler(&RestApiAbstractJob::onPostRequestResponse);
0564     });
0565 }
0566 
0567 QJsonDocument RestApiAbstractJob::convertToJsonDocument(QNetworkReply *reply)
0568 {
0569     const QByteArray data = reply->readAll();
0570     const QJsonDocument replyDocument = QJsonDocument::fromJson(data);
0571     if (replyDocument.isNull()) {
0572         qCWarning(ROCKETCHATQTRESTAPI_LOG) << " convertToJsonObject return null jsondocument. It's a bug ";
0573     }
0574     return replyDocument;
0575 }
0576 
0577 QueryParameters::QueryParameters() = default;
0578 
0579 QString QueryParameters::filter() const
0580 {
0581     return mFilter;
0582 }
0583 
0584 void QueryParameters::setFilter(const QString &filter)
0585 {
0586     mFilter = filter;
0587 }
0588 
0589 int QueryParameters::offset() const
0590 {
0591     return mOffset;
0592 }
0593 
0594 void QueryParameters::setOffset(int offset)
0595 {
0596     mOffset = offset;
0597 }
0598 
0599 int QueryParameters::count() const
0600 {
0601     return mCount;
0602 }
0603 
0604 void QueryParameters::setCount(int count)
0605 {
0606     mCount = count;
0607 }
0608 
0609 bool QueryParameters::isValid() const
0610 {
0611     return (mCount >= 0) || (mOffset >= 0) || (!mSorting.isEmpty()) || !mCustom.isEmpty() || !mSearchString.isEmpty() || !mFilter.isEmpty();
0612 }
0613 
0614 QMap<QString, QueryParameters::SortOrder> QueryParameters::sorting() const
0615 {
0616     return mSorting;
0617 }
0618 
0619 void QueryParameters::setSorting(const QMap<QString, QueryParameters::SortOrder> &sorting)
0620 {
0621     mSorting = sorting;
0622 }
0623 
0624 QString QueryParameters::type() const
0625 {
0626     return mType;
0627 }
0628 
0629 void QueryParameters::setType(const QString &type)
0630 {
0631     mType = type;
0632 }
0633 
0634 QMap<QString, QString> QueryParameters::custom() const
0635 {
0636     return mCustom;
0637 }
0638 
0639 void QueryParameters::setCustom(const QMap<QString, QString> &custom)
0640 {
0641     mCustom = custom;
0642 }
0643 
0644 void QueryParameters::generateQueryParameter(const QueryParameters &queryParameters, QUrlQuery &urlQuery)
0645 {
0646     if (queryParameters.count() >= 0) {
0647         urlQuery.addQueryItem(QStringLiteral("count"), QString::number(queryParameters.count()));
0648     }
0649     if (queryParameters.offset() >= 0) {
0650         urlQuery.addQueryItem(QStringLiteral("offset"), QString::number(queryParameters.offset()));
0651     }
0652     if (!queryParameters.filter().isEmpty()) {
0653         urlQuery.addQueryItem(QStringLiteral("filter"), queryParameters.filter());
0654     }
0655 
0656     const QMap<QString, QString> custom = queryParameters.custom();
0657     if (!custom.isEmpty()) {
0658         QMapIterator<QString, QString> i(custom);
0659         QString str;
0660         while (i.hasNext()) {
0661             i.next();
0662             if (!str.isEmpty()) {
0663                 str += QLatin1Char(',');
0664             }
0665             str += QLatin1Char('"') + i.key() + QLatin1Char('"') + QLatin1Char(':');
0666             str += QLatin1Char('"') + i.value() + QLatin1Char('"');
0667         }
0668         str = QStringLiteral("{%1}").arg(str);
0669 
0670         urlQuery.addQueryItem(QStringLiteral("query"), str);
0671     }
0672     if (!queryParameters.searchString().isEmpty()) {
0673         const QString str = QStringLiteral(R"({"name":{"$regex":"%1","$options":"i"}})").arg(queryParameters.searchString());
0674         urlQuery.addQueryItem(QStringLiteral("query"), str);
0675     }
0676 
0677     if (!queryParameters.sorting().isEmpty()) {
0678         // example    sort={"name" : -1,"status" : 1}
0679         QMapIterator<QString, QueryParameters::SortOrder> i(queryParameters.sorting());
0680         QString str;
0681         while (i.hasNext()) {
0682             i.next();
0683             if (!str.isEmpty()) {
0684                 str += QLatin1Char(',');
0685             }
0686             str += QLatin1Char('"') + i.key() + QLatin1Char('"') + QLatin1Char(':');
0687             switch (i.value()) {
0688             case QueryParameters::SortOrder::Ascendant:
0689                 str += QString::number(1);
0690                 break;
0691             case QueryParameters::SortOrder::Descendant:
0692                 str += QString::number(-1);
0693                 break;
0694             case QueryParameters::SortOrder::NoSorting:
0695                 qCWarning(ROCKETCHATQTRESTAPI_LOG) << "It's not a sorting attribute";
0696                 break;
0697             }
0698         }
0699         str = QStringLiteral("{%1}").arg(str);
0700 
0701         // It's ok for getAllMentions....
0702         urlQuery.addQueryItem(QStringLiteral("sort"), str);
0703     }
0704     if (!queryParameters.type().isEmpty()) {
0705         urlQuery.addQueryItem(QStringLiteral("type"), queryParameters.type());
0706     }
0707 }
0708 
0709 const QString &QueryParameters::searchString() const
0710 {
0711     return mSearchString;
0712 }
0713 
0714 void QueryParameters::setSearchString(const QString &newSearchString)
0715 {
0716     mSearchString = newSearchString;
0717 }
0718 
0719 #include "moc_restapiabstractjob.cpp"