9 #include <QtCore/QObject>
10 #include <QtCore/QStringBuilder>
11 #include <QtCore/QLoggingCategory>
12 #include <QtCore/QFuture>
14 #include <Quotient/converters.h>
15 #include <Quotient/quotient_common.h>
17 class QNetworkRequest;
24 enum class HttpVerb { Get, Put, Post, Delete };
28 Q_PROPERTY(QUrl requestUrl READ requestUrl CONSTANT)
29 Q_PROPERTY(
int maxRetries READ maxRetries WRITE setMaxRetries)
30 Q_PROPERTY(
int statusCode READ error NOTIFY statusChanged)
32 static QByteArray encodeIfParam(
const QString& paramPart);
34 static auto encodeIfParam(
const char (&literalPart)[N])
51 UnexpectedResponseType = 21,
52 UnexpectedResponseTypeWarning = UnexpectedResponseType,
64 RateLimited = TooManyRequests,
65 RequestNotImplemented,
66 UnsupportedRoomVersion,
73 UserDefinedError = 256
77 template <
typename... StrTs>
78 static QByteArray makePath(QByteArrayView base, StrTs&&... parts)
80 return (base % ... % encodeIfParam(std::forward<StrTs>(parts)));
91 Status(StatusCode c) : code(c) {}
92 Status(
int c, QString m) : code(c), message(std::move(m)) {}
94 static StatusCode fromHttpCode(
int httpCode);
95 static Status fromHttpCode(
int httpCode, QString msg)
97 return { fromHttpCode(httpCode), std::move(msg) };
100 bool good()
const {
return code < ErrorLevel; }
101 QDebug dumpToLog(QDebug dbg)
const;
102 friend QDebug operator<<(
const QDebug& dbg,
const Status& s)
104 return s.dumpToLog(dbg);
107 bool operator==(
const Status& other)
const
109 return code == other.code && message == other.message;
111 bool operator!=(
const Status& other)
const
113 return !operator==(other);
115 bool operator==(
int otherCode)
const
117 return code == otherCode;
119 bool operator!=(
int otherCode)
const
121 return !operator==(otherCode);
129 BaseJob(HttpVerb verb,
const QString& name, QByteArray endpoint,
130 bool needsToken =
true);
131 BaseJob(HttpVerb verb,
const QString& name, QByteArray endpoint,
132 const QUrlQuery& query, RequestData&& data = {},
133 bool needsToken =
true);
135 QUrl requestUrl()
const;
136 bool isBackground()
const;
139 Status status()
const;
142 QString statusCaption()
const;
147 QByteArray rawData(
int bytesAtMost)
const;
150 const QByteArray& rawData()
const;
159 QString rawDataSample(
int bytesAtMost = 65535)
const;
165 QJsonObject jsonData()
const;
171 QJsonArray jsonItems()
const;
177 template <
typename T>
178 T loadFromJson(
auto keyName, T&& defaultValue = {})
const
180 const auto& jv = jsonData().value(keyName);
181 return jv.isUndefined() ? std::forward<T>(defaultValue) : fromJson<T>(jv);
188 template <
typename T>
189 T takeFromJson(
auto key, T&& defaultValue = {})
191 if (
const auto& jv = takeValueFromJson(key); !jv.isUndefined())
192 return fromJson<T>(jv);
194 return std::forward<T>(defaultValue);
204 virtual QString errorString()
const;
207 QUrl errorUrl()
const;
209 int maxRetries()
const;
210 void setMaxRetries(
int newMaxRetries);
212 using duration_ms_t = std::chrono::milliseconds::rep;
214 std::chrono::seconds getCurrentTimeout()
const;
215 Q_INVOKABLE Quotient::BaseJob::duration_ms_t getCurrentTimeoutMs()
const;
216 std::chrono::seconds getNextRetryInterval()
const;
217 Q_INVOKABLE Quotient::BaseJob::duration_ms_t getNextRetryMs()
const;
218 std::chrono::milliseconds timeToRetry()
const;
219 Q_INVOKABLE Quotient::BaseJob::duration_ms_t millisToRetry()
const;
221 friend QDebug operator<<(QDebug dbg,
const BaseJob* j)
223 return dbg << j->objectName();
227 void initiate(Quotient::ConnectionData* connData,
bool inBackground);
249 void aboutToSendRequest(QNetworkRequest* req);
255 void statusChanged(Quotient::BaseJob::Status newStatus);
260 void retryScheduled(
int nextAttempt, Quotient::BaseJob::duration_ms_t inMilliseconds);
284 void finished(Quotient::BaseJob* job);
293 void result(Quotient::BaseJob* job);
297 void success(Quotient::BaseJob*);
305 void failure(Quotient::BaseJob*);
307 void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
308 void uploadProgress(qint64 bytesSent, qint64 bytesTotal);
311 using headers_t = QHash<QByteArray, QByteArray>;
313 QByteArray apiEndpoint()
const;
314 void setApiEndpoint(QByteArray apiEndpoint);
315 const headers_t& requestHeaders()
const;
316 void setRequestHeader(
const headers_t::key_type& headerName,
317 const headers_t::mapped_type& headerValue);
318 void setRequestHeaders(
const headers_t& headers);
319 QUrlQuery query()
const;
320 void setRequestQuery(
const QUrlQuery& query);
321 const RequestData& requestData()
const;
322 void setRequestData(RequestData&& data);
323 const QByteArrayList& expectedContentTypes()
const;
324 void addExpectedContentType(
const QByteArray& contentType);
325 void setExpectedContentTypes(
const QByteArrayList& contentTypes);
326 QStringList expectedKeys()
const;
327 void addExpectedKey(QString key);
328 void setExpectedKeys(
const QStringList& keys);
330 const QNetworkReply* reply()
const;
331 QNetworkReply* reply();
338 static QUrl makeRequestUrl(
const HomeserverData& hsData,
const QByteArray& encodedPath,
339 const QUrlQuery& query = {});
345 virtual void doPrepare(
const ConnectionData*);
352 virtual void onSentRequest(QNetworkReply*);
354 virtual void beforeAbandon();
372 virtual Status checkReply(
const QNetworkReply* reply)
const;
379 virtual Status prepareResult();
390 virtual Status prepareError(Status currentStatus);
402 QJsonValue takeValueFromJson(QAnyStringView key);
404 void setStatus(Status s);
405 void setStatus(
int code, QString message);
411 void forceResult(QJsonDocument resultDoc, Status s = { Success });
418 void setLoggingCategory(QMessageLogger::CategoryFunction lcf);
431 friend class ConnectionData;
432 template <
class JobT>
433 friend class JobHandle;
437 QFuture<
void> future();
445 return job && job->error() == BaseJob::Pending;
448 template <
typename JobT>
449 constexpr inline auto doCollectResponse =
nullptr;
459 template <std::derived_from<BaseJob> JobT>
460 inline auto collectResponse(JobT* job)
461 requires requires { doCollectResponse<JobT>(job); }
463 return doCollectResponse<JobT>(job);
466 template <std::derived_from<BaseJob> JobT>
467 class Mocked :
public JobT {
470 void setResult(QJsonDocument d) { JobT::forceResult(std::move(d)); }