135 lines
3.6 KiB
C++
135 lines
3.6 KiB
C++
|
//SPDX-FileCopyrightText: 2023 Yury Gubich <blue@macaw.me>
|
||
|
//SPDX-License-Identifier: GPL-3.0-or-later
|
||
|
|
||
|
#include "request.h"
|
||
|
|
||
|
#include <QJsonDocument>
|
||
|
#include <QJsonObject>
|
||
|
|
||
|
const std::map<QString, QMetaType::Type> Request::Request::resultStructure({
|
||
|
{"result", QMetaType::LongLong},
|
||
|
});
|
||
|
|
||
|
Request::Request::Request (const QUrl& url):
|
||
|
state(State::initial),
|
||
|
request(url),
|
||
|
reply(),
|
||
|
emptyResult(false)
|
||
|
{}
|
||
|
|
||
|
Request::Request::~Request () {
|
||
|
cancel();
|
||
|
}
|
||
|
|
||
|
void Request::Request::send (QNetworkAccessManager& manager) {
|
||
|
if (state != State::initial)
|
||
|
throw new std::runtime_error("An attempt to send a request in a wrong state");
|
||
|
|
||
|
initializeRequest();
|
||
|
aquireRequest(manager);
|
||
|
connect(reply.get(), &QNetworkReply::finished, this, &Request::onFinished, Qt::QueuedConnection);
|
||
|
state = State::sent;
|
||
|
}
|
||
|
|
||
|
void Request::Request::cancel () {
|
||
|
if (stateTerminal())
|
||
|
return;
|
||
|
|
||
|
state = State::cancelled;
|
||
|
if (reply)
|
||
|
reply->abort();
|
||
|
}
|
||
|
|
||
|
void Request::Request::setAuthorizationToken (const QString& token) {
|
||
|
request.setRawHeader("Authorization", "Bearer " + token.toUtf8());
|
||
|
}
|
||
|
|
||
|
void Request::Request::aquireRequest (QNetworkAccessManager& manager) {
|
||
|
reply = std::unique_ptr<QNetworkReply, NetworkReplyDeleter>(manager.get(request));
|
||
|
}
|
||
|
|
||
|
void Request::Request::initializeRequest () {
|
||
|
request.setHeader(QNetworkRequest::ContentTypeHeader, json);
|
||
|
}
|
||
|
|
||
|
std::optional<QString> Request::Request::validate () {
|
||
|
QNetworkReply::NetworkError error = reply->error();
|
||
|
|
||
|
if (error != QNetworkReply::NoError)
|
||
|
return reply->errorString();
|
||
|
else
|
||
|
return std::nullopt;
|
||
|
}
|
||
|
|
||
|
std::optional<QVariantMap> Request::Request::readResult () {
|
||
|
QVariant contentType = reply->header(QNetworkRequest::ContentTypeHeader);
|
||
|
if (!contentType.isValid() || !contentType.canConvert<QString>() || contentType.toString() != json)
|
||
|
return std::nullopt;
|
||
|
|
||
|
QByteArray data = reply->readAll();
|
||
|
QJsonDocument document = QJsonDocument::fromJson(data);
|
||
|
if (!document.isObject())
|
||
|
return std::nullopt;
|
||
|
|
||
|
QJsonObject object = document.object();
|
||
|
return object.toVariantMap();
|
||
|
}
|
||
|
|
||
|
void Request::Request::onSuccess (const QVariantMap& data) {
|
||
|
emit success(data);
|
||
|
emit done();
|
||
|
}
|
||
|
|
||
|
void Request::Request::onError (const QString& err, const std::optional<QVariantMap>& data) {
|
||
|
emit error(err, data);
|
||
|
emit done();
|
||
|
}
|
||
|
|
||
|
bool Request::Request::validateResponse (const std::optional<QVariantMap>& data, const std::map<QString, QMetaType::Type>& structure) {
|
||
|
if (!data.has_value())
|
||
|
return false;
|
||
|
|
||
|
for (const std::pair<const QString, QMetaType::Type>& pair : structure) {
|
||
|
QVariantMap::ConstIterator itr = data->find(pair.first);
|
||
|
if (itr == data->end())
|
||
|
return false;
|
||
|
|
||
|
if (itr->userType() != pair.second)
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
QUrl Request::Request::createUrl (QUrl base, const QString& path) {
|
||
|
QString startingPath = base.path();
|
||
|
base.setPath(startingPath + path);
|
||
|
return base;
|
||
|
}
|
||
|
|
||
|
bool Request::Request::stateTerminal () const {
|
||
|
return state == State::responded || state == State::cancelled;
|
||
|
}
|
||
|
|
||
|
void Request::Request::onFinished () {
|
||
|
if (state != State::sent)
|
||
|
return;
|
||
|
|
||
|
state = State::responded;
|
||
|
std::optional<QString> err = validate();
|
||
|
std::optional<QVariantMap> data;
|
||
|
if (!emptyResult)
|
||
|
data = readResult();
|
||
|
|
||
|
if (err)
|
||
|
return onError(err.value(), data);
|
||
|
|
||
|
if (emptyResult)
|
||
|
return onSuccess({});
|
||
|
|
||
|
if (data)
|
||
|
return onSuccess(data.value());
|
||
|
else
|
||
|
return onError("Error reading request result", data);
|
||
|
}
|