magpie/API/requests/request.cpp

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);
}