QtRestClient  3.0.0
A library for generic JSON-based REST-APIs, with a mechanism to map JSON to Qt objects
genericrestreply.h
1 #ifndef QTRESTCLIENT_GENERICRESTREPLY_H
2 #define QTRESTCLIENT_GENERICRESTREPLY_H
3 
4 #include "QtRestClient/qtrestclient_global.h"
5 #include "QtRestClient/restclient.h"
6 #include "QtRestClient/restreply.h"
7 #include "QtRestClient/paging_fwd.h"
8 #include "QtRestClient/metacomponent.h"
9 
10 #include <type_traits>
11 
12 #include <QtJsonSerializer/serializerbase.h>
13 
14 namespace QtRestClient {
15 
16 template <typename DataClassType, typename ErrorClassType>
18 
19 template <typename DataClassType, typename ErrorClassType>
21 
23 template <typename DataClassType, typename ErrorClassType>
25 {
26 public:
29 
31  TInstance *onFailed(std::function<void(int, ErrorClassType)> handler);
33  virtual TInstance *onFailed(QObject *scope, std::function<void(int, ErrorClassType)> handler);
35  TInstance *onSerializeException(std::function<void(QtJsonSerializer::Exception&)> handler);
37  TInstance *onAllErrors(const std::function<void(QString, int, Error)> &handler,
38  std::function<QString(ErrorClassType, int)> failureTransformer = {});
41  const std::function<void(QString, int, Error)> &handler,
42  std::function<QString(ErrorClassType, int)> failureTransformer = {});
43 
44  //overshadowing, for the right return type only...
46  TInstance *onCompleted(std::function<void(int)> handler);
48  TInstance *onCompleted(QObject *scope, std::function<void(int)> handler);
50  TInstance *onError(std::function<void(QString, int, Error)> handler);
52  virtual TInstance *onError(QObject *scope, std::function<void(QString, int, RestReply::Error)> handler);
53 #ifdef QT_RESTCLIENT_USE_ASYNC
56 #endif
59 
61  GenericRestReplyAwaitable<DataClassType, ErrorClassType> awaitable();
62 
63 protected:
65  GenericRestReplyBase(QNetworkReply *networkReply,
66  RestClient *client,
67  QObject *parent);
68 #ifdef QT_RESTCLIENT_USE_ASYNC
69  GenericRestReplyBase(const QFuture<QNetworkReply*> &networkReplyFuture,
71  RestClient *client,
72  QObject *parent);
73 #endif
74 
76  RestClient *_client;
78  std::function<void(QtJsonSerializer::Exception &)> _exceptionHandler;
79 };
80 
82 template <typename DataClassType, typename ErrorClassType = QObject*>
83 class GenericRestReply : public GenericRestReplyBase<DataClassType, ErrorClassType>
84 {
85 public:
88  RestClient *client,
89  QObject *parent = nullptr);
90 #ifdef QT_RESTCLIENT_USE_ASYNC
91  GenericRestReply(const QFuture<QNetworkReply*> &networkReplyFuture,
93  RestClient *client,
94  QObject *parent = nullptr);
95 #endif
96 
98  GenericRestReply<DataClassType, ErrorClassType> *onSucceeded(std::function<void(int, DataClassType)> handler);
100  GenericRestReply<DataClassType, ErrorClassType> *onSucceeded(QObject *scope, std::function<void(int, DataClassType)> handler);
101 };
102 
106 template <typename ErrorClassType>
107 class GenericRestReply<void, ErrorClassType> : public GenericRestReplyBase<void, ErrorClassType>
108 {
109 public:
112  RestClient *client,
113  QObject *parent = nullptr);
114 #ifdef QT_RESTCLIENT_USE_ASYNC
115  GenericRestReply(const QFuture<QNetworkReply*> &networkReplyFuture,
117  RestClient *client,
118  QObject *parent = nullptr);
119 #endif
120 
122  GenericRestReply<void, ErrorClassType> *onSucceeded(std::function<void(int)> handler);
124  GenericRestReply<void, ErrorClassType> *onSucceeded(QObject *scope, std::function<void(int)> handler);
125 };
126 
132 template <typename DataClassType, typename ErrorClassType>
133 class GenericRestReply<Paging<DataClassType>, ErrorClassType> : public GenericRestReplyBase<Paging<DataClassType>, ErrorClassType>
134 {
135 public:
138  RestClient *client,
139  QObject *parent = nullptr);
140 #ifdef QT_RESTCLIENT_USE_ASYNC
141  GenericRestReply(const QFuture<QNetworkReply*> &networkReplyFuture,
143  RestClient *client,
144  QObject *parent = nullptr);
145 #endif
146 
148  GenericRestReply<Paging<DataClassType>, ErrorClassType> *onSucceeded(std::function<void(int, Paging<DataClassType>)> handler);
150  GenericRestReply<Paging<DataClassType>, ErrorClassType> *onSucceeded(QObject *scope, std::function<void(int, Paging<DataClassType>)> handler);
151 
153  GenericRestReply<Paging<DataClassType>, ErrorClassType> *iterate(std::function<bool(DataClassType, qint64)> iterator,
154  qint64 to = -1,
155  qint64 from = 0);
157  GenericRestReply<Paging<DataClassType>, ErrorClassType> *iterate(QObject *scope,
158  std::function<bool(DataClassType, qint64)> iterator,
159  qint64 to = -1,
160  qint64 from = 0);
161 
163  GenericRestReply<Paging<DataClassType>, ErrorClassType> *onFailed(QObject *scope, std::function<void(int, ErrorClassType)> handler) final;
165  GenericRestReply<Paging<DataClassType>, ErrorClassType> *onError(QObject *scope, std::function<void(QString, int, RestReply::Error)> handler) final;
166 
167 private:
168  std::function<void(int, ErrorClassType)> _failureHandler;
169  std::function<void(QString, int, RestReply::Error)> _errorHandler;
170 };
171 
172 } //end namespace, because of include!
173 
174 //include after delecation, to allow foreward declared types
175 #include "QtRestClient/paging.h"
176 
177 namespace QtRestClient {
178 
179 // ------------- Implementation base class -------------
180 
181 template <typename DataClassType, typename ErrorClassType>
183 {
184  return onFailed(this, std::move(handler));
185 }
186 
187 template <typename DataClassType, typename ErrorClassType>
189 {
190  RestReply::onFailed(scope, [this, xHandler = std::move(handler)](int code, const DataType &value){
191  std::visit(__private::overload {
192  [&](std::nullopt_t) {
193  xHandler(code, ErrorClassType{});
194  },
195  [&](auto data) {
196  try {
197  xHandler(code, _client->serializer()->deserializeGeneric(data, qMetaTypeId<ErrorClassType>()).template value<ErrorClassType>());
199  if (_exceptionHandler)
200  _exceptionHandler(e);
201  xHandler(code, ErrorClassType{});
202  }
203  }
204  }, value);
205  });
206  return static_cast<TInstance*>(this);
207 }
208 
209 template <typename DataClassType, typename ErrorClassType>
211 {
212  _exceptionHandler = std::move(handler);
213  return static_cast<TInstance*>(this);
214 }
215 
216 template <typename DataClassType, typename ErrorClassType>
217 typename GenericRestReplyBase<DataClassType, ErrorClassType>::TInstance *GenericRestReplyBase<DataClassType, ErrorClassType>::onAllErrors(const std::function<void (QString, int, Error)> &handler, std::function<QString (ErrorClassType, int)> failureTransformer)
218 {
219  return onAllErrors(this, handler, std::move(failureTransformer));
220 }
221 
222 template <typename DataClassType, typename ErrorClassType>
223 typename GenericRestReplyBase<DataClassType, ErrorClassType>::TInstance *GenericRestReplyBase<DataClassType, ErrorClassType>::onAllErrors(QObject *scope, const std::function<void (QString, int, Error)> &handler, std::function<QString (ErrorClassType, int)> failureTransformer)
224 {
225  this->onSerializeException([handler](QtJsonSerializer::Exception &exception){
226  handler(QString::fromUtf8(exception.what()), 0, Error::Deserialization);
227  });
228  this->onFailed(scope, [handler, xFt = std::move(failureTransformer)](int code, ErrorClassType obj){
229  if (xFt)
230  handler(xFt(obj, code), code, Error::Failure);
231  else
232  handler({}, code, Error::Failure);
233  __private::MetaComponent<ErrorClassType>::deleteLater(obj);
234  });
235  this->onError(scope, handler);
236  return static_cast<TInstance*>(this);
237 }
238 
239 template <typename DataClassType, typename ErrorClassType>
241 {
242  return onCompleted(this, std::move(handler));
243 }
244 
245 template <typename DataClassType, typename ErrorClassType>
247 {
248  RestReply::onCompleted(scope, std::move(handler));
249  return static_cast<TInstance*>(this);
250 }
251 
252 template <typename DataClassType, typename ErrorClassType>
254 {
255  return onError(this, std::move(handler));
256 }
257 
258 template <typename DataClassType, typename ErrorClassType>
260 {
261  RestReply::onError(scope, std::move(handler));
262  return static_cast<TInstance*>(this);
263 }
264 
265 #ifdef QT_RESTCLIENT_USE_ASYNC
266 template<typename DataClassType, typename ErrorClassType>
268 {
269  RestReply::makeAsync(threadPool);
270  return static_cast<TInstance*>(this);
271 }
272 #endif
273 
274 template <typename DataClassType, typename ErrorClassType>
276 {
278  return static_cast<TInstance*>(this);
279 }
280 
281 #ifdef QT_RESTCLIENT_USE_ASYNC
282 template <typename DataClassType, typename ErrorClassType>
284  RestReply{networkReply, client->asyncPool(), parent},
285  _client{client}
286 {}
287 
288 template<typename DataClassType, typename ErrorClassType>
289 GenericRestReplyBase<DataClassType, ErrorClassType>::GenericRestReplyBase(const QFuture<QNetworkReply*> &networkReplyFuture, RestClient *client, QObject *parent) :
290  RestReply{networkReplyFuture, client->asyncPool(), parent},
291  _client{client}
292 {}
293 #else
294 template <typename DataClassType, typename ErrorClassType>
295 GenericRestReplyBase<DataClassType, ErrorClassType>::GenericRestReplyBase(QNetworkReply *networkReply, RestClient *client, QObject *parent) :
296  RestReply{networkReply, parent},
297  _client{client}
298 {}
299 #endif
300 
301 // ------------- Implementation Single Element -------------
302 
303 template<typename DataClassType, typename ErrorClassType>
305  GenericRestReplyBase<DataClassType, ErrorClassType>{networkReply, client, parent}
306 {}
307 
308 #ifdef QT_RESTCLIENT_USE_ASYNC
309 template<typename DataClassType, typename ErrorClassType>
311  GenericRestReplyBase<DataClassType, ErrorClassType>{networkReplyFuture, client, parent}
312 {}
313 #endif
314 
315 template<typename DataClassType, typename ErrorClassType>
317 {
318  return onSucceeded(this, std::move(handler));
319 }
320 
321 template<typename DataClassType, typename ErrorClassType>
323 {
324  RestReply::onSucceeded(scope, [this, xFn = std::move(handler)](int code, const RestReply::DataType &value){
325  try {
326  std::visit(__private::overload {
327  [&](std::nullopt_t) {
328  xFn(code, DataClassType{});
329  },
330  [&](const auto &data) {
331  xFn(code, this->_client->serializer()->deserializeGeneric(data, qMetaTypeId<DataClassType>()).template value<DataClassType>());
332  }
333  }, value);
335  if (this->_exceptionHandler)
336  this->_exceptionHandler(e);
337  }
338  });
339  return this;
340 }
341 
342 // ------------- Implementation void -------------
343 
344 template<typename ErrorClassType>
346  GenericRestReplyBase<void, ErrorClassType>{networkReply, client, parent}
347 {
348  this->setAllowEmptyReplies(true);
349 }
350 
351 #ifdef QT_RESTCLIENT_USE_ASYNC
352 template<typename ErrorClassType>
354  GenericRestReplyBase<void, ErrorClassType>{networkReplyFuture, client, parent}
355 {
356  this->setAllowEmptyReplies(true);
357 }
358 #endif
359 
360 template<typename ErrorClassType>
362 {
363  return onSucceeded(this, std::move(handler));
364 }
365 
366 template<typename ErrorClassType>
368 {
369  RestReply::onSucceeded(scope, std::move(handler));
370  return this;
371 }
372 
373 // ------------- Implementation Paging of Elements -------------
374 
375 template<typename DataClassType, typename ErrorClassType>
377  GenericRestReplyBase<Paging<DataClassType>, ErrorClassType>{networkReply, client, parent}
378 {}
379 
380 #ifdef QT_RESTCLIENT_USE_ASYNC
381 template<typename DataClassType, typename ErrorClassType>
382 GenericRestReply<Paging<DataClassType>, ErrorClassType>::GenericRestReply(const QFuture<QNetworkReply*> &networkReplyFuture, RestClient *client, QObject *parent) :
383  GenericRestReplyBase<Paging<DataClassType>, ErrorClassType>{networkReplyFuture, client, parent}
384 {}
385 #endif
386 
387 template<typename DataClassType, typename ErrorClassType>
388 GenericRestReply<Paging<DataClassType>, ErrorClassType> *GenericRestReply<Paging<DataClassType>, ErrorClassType>::onSucceeded(std::function<void (int, Paging<DataClassType>)> handler)
389 {
390  return onSucceeded(this, handler);
391 }
392 
393 template<typename DataClassType, typename ErrorClassType>
394 GenericRestReply<Paging<DataClassType>, ErrorClassType> *GenericRestReply<Paging<DataClassType>, ErrorClassType>::onSucceeded(QObject *scope, std::function<void (int, Paging<DataClassType>)> handler)
395 {
396  RestReply::onSucceeded(scope, [this, xFn = std::move(handler)](int code, const RestReply::DataType &value){
397  try {
398  std::visit(__private::overload {
399  [&](std::nullopt_t) {
400  xFn(code, Paging<DataClassType>{});
401  },
402  [&](const auto &data) {
403  auto iPaging = this->_client->pagingFactory()->createPaging(this->_client->serializer(), data);
404  auto pData = this->_client->serializer()->deserializeGeneric(std::visit(__private::overload {
405  [](const QCborArray &data) -> std::variant<QCborValue, QJsonValue> {
406  return QCborValue{data};
407  },
408  [](const QJsonArray &data) -> std::variant<QCborValue, QJsonValue> {
409  return QJsonValue{data};
410  }
411  }, iPaging->items()),
412  qMetaTypeId<QList<DataClassType>>())
413  .template value<QList<DataClassType>>();
414  xFn(code, Paging<DataClassType>(iPaging, std::move(pData), this->_client));
415  }
416  }, value);
418  if (this->_exceptionHandler)
419  this->_exceptionHandler(e);
420  }
421  });
422  return this;
423 }
424 
441 template<typename DataClassType, typename ErrorClassType>
442 GenericRestReply<Paging<DataClassType>, ErrorClassType> *GenericRestReply<Paging<DataClassType>, ErrorClassType>::iterate(std::function<bool (DataClassType, qint64)> iterator, qint64 to, qint64 from)
443 {
444  return onSucceeded(this, [this, it = std::move(iterator), to, from](int, const Paging<DataClassType> &paging) {
445  if (paging.isValid())
446  paging.iterate(it, this->_failureHandler, this->_errorHandler, this->_exceptionHandler, to, from);
447  });
448 }
449 
467 template<typename DataClassType, typename ErrorClassType>
468 GenericRestReply<Paging<DataClassType>, ErrorClassType> *GenericRestReply<Paging<DataClassType>, ErrorClassType>::iterate(QObject *scope, std::function<bool (DataClassType, qint64)> iterator, qint64 to, qint64 from)
469 {
470  return onSucceeded(scope, [this, scope, it = std::move(iterator), to, from](int, const Paging<DataClassType> &paging){
471  if (paging.isValid())
472  paging.iterate(scope, it, this->_failureHandler, this->_errorHandler, this->_exceptionHandler, to, from);
473  });
474 }
475 
476 template<typename DataClassType, typename ErrorClassType>
477 GenericRestReply<Paging<DataClassType>, ErrorClassType> *GenericRestReply<Paging<DataClassType>, ErrorClassType>::onFailed(QObject *scope, std::function<void (int, ErrorClassType)> handler)
478 {
479  _failureHandler = std::move(handler);
480  return GenericRestReplyBase<Paging<DataClassType>, ErrorClassType>::onFailed(scope, _failureHandler);
481 }
482 
483 template<typename DataClassType, typename ErrorClassType>
484 GenericRestReply<Paging<DataClassType>, ErrorClassType> *GenericRestReply<Paging<DataClassType>, ErrorClassType>::onError(QObject *scope, std::function<void (QString, int, RestReply::Error)> handler)
485 {
486  _errorHandler = std::move(handler);
487  return GenericRestReplyBase<Paging<DataClassType>, ErrorClassType>::onError(scope, _errorHandler);
488 }
489 
490 }
491 
492 #endif // QTRESTCLIENT_GENERICRESTREPLY_H
QtJsonSerializer::Exception::what
const char * what() const noexcept final
QtRestClient::GenericRestReplyBase::TInstance
GenericRestReply< DataClassType, ErrorClassType > TInstance
The specialized GenericRestReply that is associated with the reply base.
Definition: genericrestreply.h:28
QString::fromUtf8
QString fromUtf8(const char *str, int size)
QtRestClient
The Namespace containing all classes of the QtRestClient module.
Definition: genericrestreply.h:14
QtRestClient::RestReply::makeAsync
Q_INVOKABLE RestReply * makeAsync(QThreadPool *threadPool=QThreadPool::globalInstance())
WRITE accessor for RestReply::async.
QtRestClient::RestReply::Error
Error
Defines the different possible error types.
Definition: restreply.h:45
QtRestClient::Paging::iterate
void iterate(const std::function< bool(T, qint64)> &iterator, qint64 to=-1, qint64 from=0) const
Iterates over all paging objects.
Definition: paging.h:132
QNetworkReply
QtRestClient::Paging::isValid
bool isValid() const
Returns true, if the current paging object is a valid one.
Definition: paging.h:58
QThreadPool::globalInstance
QThreadPool * globalInstance()
QtRestClient::GenericRestReplyAwaitable
A helper class to be used with QtCoroutines to await a generic rest reply.
Definition: genericrestreply.h:17
QtRestClient::GenericRestReply< Paging< DataClassType >, ErrorClassType >
A class to handle generic replies for generic requests.
Definition: genericrestreply.h:133
QList
QtRestClient::RestReply::onCompleted
RestReply * onCompleted(TFn &&handler)
Set a handler to be called when the request was completed, regardless of success or failure.
Definition: restreply.h:227
QtRestClient::GenericRestReplyBase::awaitable
GenericRestReplyAwaitable< DataClassType, ErrorClassType > awaitable()
Returns an awaitable object for this reply.
Definition: restreplyawaitable.h:328
QtRestClient::RestReply::onFailed
RestReply * onFailed(TFn &&handler)
Set a handler to be called if the request failed.
Definition: restreply.h:212
QtRestClient::RestReply::onSucceeded
RestReply * onSucceeded(TFn &&handler)
Set a handler to be called if the request succeeded.
Definition: restreply.h:197
QJsonValue
QJsonArray
QString
QtRestClient::GenericRestReplyBase::onError
TInstance * onError(std::function< void(QString, int, Error)> handler)
Set a handler to be called if a network error or json parse error occures.
Definition: genericrestreply.h:253
QtRestClient::RestReply::networkReply
Q_INVOKABLE QNetworkReply * networkReply() const
Returns the network reply associated with the rest reply.
QThreadPool
QtRestClient::RestReply::disableAutoDelete
Q_INVOKABLE RestReply * disableAutoDelete()
WRITE accessor for RestReply::autoDelete.
Definition: restreply.h:112
QtRestClient::GenericRestReplyBase::makeAsync
TInstance * makeAsync(QThreadPool *threadPool=QThreadPool::globalInstance())
WRITE accessor for RestReply::async.
Definition: genericrestreply.h:267
QtRestClient::GenericRestReply::GenericRestReply
GenericRestReply(QNetworkReply *networkReply, RestClient *client, QObject *parent=nullptr)
Creates a generic reply based on a network reply and for a client.
Definition: genericrestreply.h:304
QtRestClient::GenericRestReply
A class to handle generic replies for generic requests.
Definition: genericrestreply.h:20
QCborValue
QtRestClient::Paging
A class to access generic paging objects.
Definition: paging_fwd.h:30
QFuture
QtRestClient::GenericRestReplyBase::onFailed
TInstance * onFailed(std::function< void(int, ErrorClassType)> handler)
Set a handler to be called if the request failed.
Definition: genericrestreply.h:182
QtRestClient::GenericRestReplyBase::disableAutoDelete
TInstance * disableAutoDelete()
WRITE accessor for RestReply::autoDelete.
Definition: genericrestreply.h:275
QtRestClient::RestReply::onError
RestReply * onError(std::function< void(QString, int, Error)> handler)
Set a handler to be called if a network error or json parse error occures.
QtRestClient::GenericRestReply::onSucceeded
GenericRestReply< DataClassType, ErrorClassType > * onSucceeded(std::function< void(int, DataClassType)> handler)
Set a handler to be called if the request succeeded.
Definition: genericrestreply.h:316
QtRestClient::RestReply::DataType
std::variant< std::nullopt_t, QCborValue, QJsonValue > DataType
Internal datatype that unites JSON and CBOR data in a typesafe union.
Definition: restreply.h:39
QtRestClient::GenericRestReplyBase::onSerializeException
TInstance * onSerializeException(std::function< void(QtJsonSerializer::Exception &)> handler)
Set a handler to be called on deserialization exceptions.
Definition: genericrestreply.h:210
QtJsonSerializer::Exception
QtRestClient::GenericRestReplyBase
The base class for GenericRestReply specializations.
Definition: genericrestreply.h:24
QObject
QtRestClient::GenericRestReplyBase::onCompleted
TInstance * onCompleted(std::function< void(int)> handler)
Set a handler to be called when the request was completed, regardless of success or failure.
Definition: genericrestreply.h:240
QtRestClient::RestReply
A class to handle replies for JSON requests.
Definition: restreply.h:23
QtRestClient::RestClient
A class to define access to an API, with general settings.
Definition: restclient.h:30
QtRestClient::RestClient::asyncPool
QThreadPool asyncPool
Holds a thread pool to be used by all replies created via this clients classes.
Definition: restclient.h:57
QCborArray
QtRestClient::GenericRestReply< void, ErrorClassType >
A class to handle generic replies for generic requests.
Definition: genericrestreply.h:107
QtRestClient::GenericRestReplyBase::onAllErrors
TInstance * onAllErrors(const std::function< void(QString, int, Error)> &handler, std::function< QString(ErrorClassType, int)> failureTransformer={})
Set a handler to be called if the request did not succeed.
Definition: genericrestreply.h:217
QObject::parent
QObject * parent() const const
QtJsonSerializer::DeserializationException