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 }