File indexing completed on 2024-12-08 04:17:52

0001 /*
0002     SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "kgraphql.h"
0008 #include "kgraphqlminimizer_p.h"
0009 
0010 #include <QFile>
0011 #include <QJsonArray>
0012 #include <QJsonDocument>
0013 #include <QJsonObject>
0014 #include <QNetworkAccessManager>
0015 #include <QNetworkReply>
0016 #include <QNetworkRequest>
0017 #include <QUrl>
0018 
0019 class KGraphQLRequestPrivate : public QSharedData
0020 {
0021 public:
0022     KGraphQLRequestPrivate();
0023 
0024     QNetworkRequest m_request;
0025     QString m_query;
0026     QJsonObject m_variables;
0027 };
0028 
0029 KGraphQLRequestPrivate::KGraphQLRequestPrivate()
0030 {
0031     m_request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json"));
0032 }
0033 
0034 KGraphQLRequest::KGraphQLRequest() :
0035     d(new KGraphQLRequestPrivate)
0036 {
0037 }
0038 
0039 KGraphQLRequest::KGraphQLRequest(const QUrl& url) :
0040     d(new KGraphQLRequestPrivate)
0041 {
0042     d->m_request.setUrl(url);
0043 }
0044 
0045 KGraphQLRequest::KGraphQLRequest(const KGraphQLRequest&) = default;
0046 KGraphQLRequest::KGraphQLRequest(KGraphQLRequest &&) noexcept = default;
0047 KGraphQLRequest::~KGraphQLRequest() = default;
0048 KGraphQLRequest& KGraphQLRequest::operator=(const KGraphQLRequest&) = default;
0049 
0050 void KGraphQLRequest::setUrl(const QUrl &url)
0051 {
0052     d.detach();
0053     d->m_request.setUrl(url);
0054 }
0055 
0056 void KGraphQLRequest::setQuery(const QString &query)
0057 {
0058     d.detach();
0059     d->m_query = query;
0060 }
0061 
0062 void KGraphQLRequest::setQueryFromFile(const QString &fileName)
0063 {
0064     QFile f(fileName);
0065     if (!f.open(QFile::ReadOnly)) {
0066         qWarning() << "Failed to load GraphQL query from file:" << fileName << f.errorString();
0067         return;
0068     }
0069 
0070     d.detach();
0071     KGraphQLMinimizer m;
0072     d->m_query = QString::fromUtf8(m.minimizeQuery(f.readAll()));
0073 }
0074 
0075 void KGraphQLRequest::setVariable(const QString &name, const QJsonValue &value)
0076 {
0077     d.detach();
0078     d->m_variables.insert(name, value);
0079 }
0080 
0081 QNetworkRequest& KGraphQLRequest::networkRequest()
0082 {
0083     return d->m_request;
0084 }
0085 
0086 const QNetworkRequest& KGraphQLRequest::networkRequest() const
0087 {
0088     return d->m_request;
0089 }
0090 
0091 QByteArray KGraphQLRequest::rawData() const
0092 {
0093     QJsonObject obj;
0094     obj.insert(QStringLiteral("query"), d->m_query);
0095     if (!d->m_variables.isEmpty()) {
0096         obj.insert(QStringLiteral("variables"), d->m_variables);
0097     }
0098     return QJsonDocument(obj).toJson(QJsonDocument::Compact);
0099 }
0100 
0101 
0102 class KGraphQLReplyPrivate
0103 {
0104 public:
0105     QNetworkReply *m_reply = nullptr;
0106     QByteArray m_data;
0107     QJsonObject m_result;
0108 };
0109 
0110 KGraphQLReply::KGraphQLReply(QNetworkReply *reply)
0111     : d(new KGraphQLReplyPrivate)
0112 {
0113     d->m_reply = reply;
0114     if (d->m_reply->error() == QNetworkReply::NoError || d->m_reply->error() == QNetworkReply::InternalServerError) {
0115         d->m_data = reply->readAll();
0116         d->m_result = QJsonDocument::fromJson(d->m_data).object();
0117     }
0118 }
0119 
0120 KGraphQLReply::KGraphQLReply(KGraphQLReply &&) noexcept = default;
0121 
0122 KGraphQLReply::~KGraphQLReply()
0123 {
0124     d->m_reply->deleteLater();
0125 }
0126 
0127 KGraphQLReply::Error KGraphQLReply::error() const
0128 {
0129     if (d->m_reply->error() != QNetworkReply::NoError && d->m_result.isEmpty()) {
0130         return NetworkError;
0131     }
0132     if (d->m_result.contains(QLatin1String("data"))) {
0133         return NoError;
0134     }
0135     return QueryError;
0136 }
0137 
0138 QString KGraphQLReply::errorString() const
0139 {
0140     switch (error()) {
0141         case NoError:
0142             return {};
0143         case NetworkError:
0144             return d->m_reply->errorString();
0145         case QueryError:
0146         {
0147             const auto errorArray = d->m_result.value(QLatin1String("errors")).toArray();
0148             QStringList errors;
0149             errors.reserve(errorArray.size());
0150             for (const auto &errorValue : errorArray) {
0151                 const auto errorObj = errorValue.toObject();
0152                 const auto msg = QString::fromUtf8("%1 (line: %2, column: %3)")
0153                     .arg(errorObj.value(QLatin1String("message")).toString())
0154                     .arg(errorObj.value(QLatin1String("locations")).toArray().at(0).toObject().value(QLatin1String("line")).toInt())
0155                     .arg(errorObj.value(QLatin1String("locations")).toArray().at(0).toObject().value(QLatin1String("column")).toInt());
0156                 errors.push_back(msg);
0157             }
0158             return errors.join(QLatin1Char('\n'));
0159         }
0160     }
0161     return {};
0162 }
0163 
0164 QJsonObject KGraphQLReply::data() const
0165 {
0166     return d->m_result.value(QLatin1String("data")).toObject();
0167 }
0168 
0169 QNetworkReply* KGraphQLReply::networkReply() const
0170 {
0171     return d->m_reply;
0172 }
0173 
0174 QByteArray KGraphQLReply::rawData() const
0175 {
0176     return d->m_data;
0177 }
0178 
0179 
0180 void KGraphQL::query(const KGraphQLRequest &request, QNetworkAccessManager *nam, const std::function<void(const KGraphQLReply&)> &callback, QObject *replyParent)
0181 {
0182     auto reply = nam->post(request.networkRequest(), request.rawData());
0183     reply->setParent(replyParent);
0184     QObject::connect(reply, &QNetworkReply::finished, nam, [reply, callback]() {
0185         callback(KGraphQLReply(reply));
0186         reply->deleteLater();
0187     });
0188 }