QtRestClient  3.0.0
A library for generic JSON-based REST-APIs, with a mechanism to map JSON to Qt objects
restclient.cpp
1 #include "restclient.h"
2 #include "restclient_p.h"
3 #include "restclass.h"
4 #include "requestbuilder_p.h"
5 #include <QtCore/QBitArray>
6 #include <QtCore/QCoreApplication>
7 #include <QtCore/QRegularExpression>
8 #include <QtCore/QUuid>
9 using namespace QtRestClient;
10 
11 #ifndef Q_RESTCLIENT_NO_JSON_SERIALIZER
12 #include "paging_fwd.h"
13 #include <QtJsonSerializer/JsonSerializer>
14 #include <QtJsonSerializer/CborSerializer>
15 using namespace QtJsonSerializer;
16 #endif
17 
20 {}
21 
23  RestClient{*new RestClientPrivate{}, parent}
24 {
25  setupNam();
26  setDataMode(dataMode);
27 }
28 
29 #ifndef Q_RESTCLIENT_NO_JSON_SERIALIZER
31  RestClient{*new RestClientPrivate{}, parent}
32 {
33  setupNam();
34  setSerializer(serializer);
35 }
36 #endif
37 
39 {
40  return new RestClass{this, path.split(QLatin1Char('/'), QString::SkipEmptyParts), parent};
41 }
42 
44 {
45  Q_D(const RestClient);
46  return d->rootClass;
47 }
48 
50 {
51  Q_D(const RestClient);
52  QReadLocker _{d->threadLock};
53  return d->nam;
54 }
55 
56 #ifndef Q_RESTCLIENT_NO_JSON_SERIALIZER
58 {
59  Q_D(const RestClient);
60  QReadLocker _{d->threadLock};
61  return d->serializer;
62 }
63 #endif
64 
66 {
67  Q_D(const RestClient);
68  QReadLocker _{d->threadLock};
69  return d->pagingFactory.data();
70 }
71 
73 {
74  Q_D(const RestClient);
75  QReadLocker _{d->threadLock};
76 #ifndef Q_RESTCLIENT_NO_JSON_SERIALIZER
77  return d->serializer && d->serializer->metaObject()->inherits(&CborSerializer::staticMetaObject) ?
80 #else
81  return d->dataMode;
82 #endif
83 }
84 
86 {
87  Q_D(const RestClient);
88  QReadLocker _{d->threadLock};
89  return d->baseUrl;
90 }
91 
93 {
94  Q_D(const RestClient);
95  QReadLocker _{d->threadLock};
96  return d->apiVersion;
97 }
98 
100 {
101  Q_D(const RestClient);
102  QReadLocker _{d->threadLock};
103  return d->headers;
104 }
105 
107 {
108  Q_D(const RestClient);
109  QReadLocker _{d->threadLock};
110  return d->query;
111 }
112 
114 {
115  Q_D(const RestClient);
116  QReadLocker _{d->threadLock};
117  return d->attribs;
118 }
119 
121 {
122  Q_D(const RestClient);
123  return d->threadLock;
124 }
125 
126 #ifndef QT_NO_SSL
128 {
129  Q_D(const RestClient);
130  QReadLocker _{d->threadLock};
131  return d->sslConfig;
132 }
133 #endif
134 
135 #ifdef QT_RESTCLIENT_USE_ASYNC
137 {
138  Q_D(const RestClient);
139  QReadLocker _{d->threadLock};
140  return d->asyncPool;
141 }
142 #endif
143 
145 {
146  Q_D(const RestClient);
147  QReadLocker _{d->threadLock};
148  RequestBuilder builder{d->baseUrl, d->nam};
149 
150  builder.setVersion(d->apiVersion)
151  .setAttributes(d->attribs)
152 #ifndef QT_NO_SSL
153  .setSslConfig(d->sslConfig)
154 #endif
155  .addHeaders(d->headers)
156  .addParameters(d->query);
157 
158  switch (dataMode()) {
159  case DataMode::Cbor:
160  builder.setAccept(RequestBuilderPrivate::ContentTypeCbor);
161  break;
162  case DataMode::Json:
163  builder.setAccept(RequestBuilderPrivate::ContentTypeJson);
164  break;
165  default:
166  Q_UNREACHABLE();
167  }
168 
169  return builder;
170 }
171 
173 {
174  Q_D(RestClient);
175  QWriteLocker _{d->threadLock};
176  d->nam->deleteLater();
177  d->nam = manager;
178  manager->setParent(this);
179 }
180 
181 #ifndef Q_RESTCLIENT_NO_JSON_SERIALIZER
183 {
184  Q_D(RestClient);
185  QWriteLocker _{d->threadLock};
186  if (d->serializer == serializer)
187  return;
188 
189  if (d->serializer)
190  d->serializer->deleteLater();
191  d->serializer = serializer;
192  serializer->setParent(this);
193  _.unlock();
195 }
196 #endif
197 
199 {
200  Q_D(RestClient);
201  QWriteLocker _{d->threadLock};
202  d->pagingFactory.reset(factory);
203 }
204 
206 {
207  Q_D(RestClient);
208 #ifndef Q_RESTCLIENT_NO_JSON_SERIALIZER
209  if (this->dataMode() == dataMode && d->serializer)
210  return;
211 
212  SerializerBase *ser;
213  switch (dataMode) {
214  case DataMode::Cbor:
215  ser = new CborSerializer{this};
216  break;
217  case DataMode::Json:
218  ser = new JsonSerializer{this};
219  break;
220  default:
221  Q_UNREACHABLE();
222  }
223  ser->setAllowDefaultNull(true);
224  setSerializer(ser);
225 #else
226  QWriteLocker _{d->threadLock};
227  if (d->dataMode == dataMode)
228  return;
229 
230  d->dataMode = dataMode;
231  Q_EMIT dataModeChanged(d->dataMode, {});
232 #endif
233 }
234 
236 {
237  Q_D(RestClient);
238  QWriteLocker _{d->threadLock};
239  if (d->baseUrl == baseUrl)
240  return;
241 
242  d->baseUrl = std::move(baseUrl);
243  Q_EMIT baseUrlChanged(d->baseUrl, {});
244 }
245 
247 {
248  Q_D(RestClient);
249  QWriteLocker _{d->threadLock};
250  if (d->apiVersion == apiVersion)
251  return;
252 
253  d->apiVersion = std::move(apiVersion);
254  Q_EMIT apiVersionChanged(d->apiVersion, {});
255 }
256 
258 {
259  Q_D(RestClient);
260  QWriteLocker _{d->threadLock};
261  if (d->headers == globalHeaders)
262  return;
263 
264  d->headers = std::move(globalHeaders);
265  Q_EMIT globalHeadersChanged(d->headers, {});
266 }
267 
269 {
270  Q_D(RestClient);
271  QWriteLocker _{d->threadLock};
272  if (d->query == globalParameters)
273  return;
274 
275  d->query = std::move(globalParameters);
276  Q_EMIT globalParametersChanged(d->query, {});
277 }
278 
280 {
281  Q_D(RestClient);
282  QWriteLocker _{d->threadLock};
283  if (d->attribs == requestAttributes)
284  return;
285 
286  d->attribs = std::move(requestAttributes);
287  Q_EMIT requestAttributesChanged(d->attribs, {});
288 }
289 
291 {
292  Q_D(RestClient);
293  QWriteLocker _{d->threadLock};
294  d->attribs.insert(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
295  d->attribs.insert(QNetworkRequest::SpdyAllowedAttribute, true);
296  d->attribs.insert(QNetworkRequest::HTTP2AllowedAttribute, true);
297  Q_EMIT requestAttributesChanged(d->attribs, {});
298 }
299 
300 void RestClient::setThreaded(bool threaded)
301 {
302  Q_D(RestClient);
303  if (static_cast<bool>(d->threadLock) == threaded)
304  return;
305 
306  if (threaded)
307  d->threadLock.storeRelease(new QReadWriteLock{QReadWriteLock::Recursive});
308  else {
309  auto ptr = d->threadLock.fetchAndStoreOrdered(nullptr);
310  delete ptr;
311  }
312  Q_EMIT threadedChanged(d->threadLock, {});
313 }
314 
315 #ifndef QT_NO_SSL
317 {
318  Q_D(RestClient);
319  QWriteLocker _{d->threadLock};
320  if (d->sslConfig == sslConfiguration)
321  return;
322 
323  d->sslConfig = std::move(sslConfiguration);
324  Q_EMIT sslConfigurationChanged(d->sslConfig, {});
325 }
326 #endif
327 
328 #ifdef QT_RESTCLIENT_USE_ASYNC
330 {
331  Q_D(RestClient);
332  QWriteLocker _{d->threadLock};
333  if (d->asyncPool == asyncPool)
334  return;
335 
336  d->asyncPool = asyncPool;
337  if (d->asyncPool)
338  setThreaded(true);
339  Q_EMIT asyncPoolChanged(d->asyncPool, {});
340 }
341 #endif
342 
343 void RestClient::addGlobalHeader(const QByteArray &name, const QByteArray &value)
344 {
345  Q_D(RestClient);
346  QWriteLocker _{d->threadLock};
347  d->headers.insert(name, value);
348  Q_EMIT globalHeadersChanged(d->headers, {});
349 }
350 
352 {
353  Q_D(RestClient);
354  QWriteLocker _{d->threadLock};
355  if(d->headers.remove(name) > 0)
356  Q_EMIT globalHeadersChanged(d->headers, {});
357 }
358 
359 void RestClient::addGlobalParameter(const QString &name, const QString &value)
360 {
361  Q_D(RestClient);
362  QWriteLocker _{d->threadLock};
363  d->query.addQueryItem(name, value);
364  Q_EMIT globalParametersChanged(d->query, {});
365 }
366 
368 {
369  Q_D(RestClient);
370  QWriteLocker _{d->threadLock};
371  d->query.removeQueryItem(name);
372  Q_EMIT globalParametersChanged(d->query, {});
373 }
374 
375 void RestClient::addRequestAttribute(QNetworkRequest::Attribute attribute, const QVariant &value)
376 {
377  Q_D(RestClient);
378  QWriteLocker _{d->threadLock};
379  d->attribs.insert(attribute, value);
380  Q_EMIT requestAttributesChanged(d->attribs, {});
381 }
382 
383 void RestClient::removeRequestAttribute(QNetworkRequest::Attribute attribute)
384 {
385  Q_D(RestClient);
386  QWriteLocker _{d->threadLock};
387  d->attribs.remove(attribute);
388  Q_EMIT requestAttributesChanged(d->attribs, {});
389 }
390 
391 RestClient::RestClient(RestClientPrivate &dd, QObject *parent) :
392  QObject{dd, parent}
393 {
394  Q_D(RestClient);
395  d->pagingFactory.reset(new StandardPagingFactory{});
396  d->rootClass = new RestClass{this, {}, this};
397 }
398 
399 void RestClient::setupNam()
400 {
401  Q_D(RestClient);
402  Q_ASSERT_X(!d->nam, Q_FUNC_INFO, "RestClient::setupNam can only be called once");
403  d->nam = new QNetworkAccessManager{this};
404  d->nam->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
405 }
406 
407 // ------------- Private Implementation -------------
408 
409 #ifndef DOXYGEN_RUN
410 QReadWriteLock RestClientPrivate::globalApiLock;
411 QHash<QString, RestClient*> RestClientPrivate::globalApis;
412 
413 RestClientPrivate::~RestClientPrivate()
414 {
415  auto ptr = threadLock.fetchAndStoreOrdered(nullptr);
416  delete ptr;
417 }
418 #endif
419 
420 // ------------- Global header implementation -------------
421 
422 Q_LOGGING_CATEGORY(QtRestClient::logGlobal, "qt.restclient");
423 #ifndef Q_RESTCLIENT_NO_JSON_SERIALIZER
424 Q_LOGGING_CATEGORY(QtRestClient::logPaging, "qt.restclient.Paging")
425 #endif
426 
437 {
438  QWriteLocker _{&RestClientPrivate::globalApiLock};
439  if (RestClientPrivate::globalApis.contains(name))
440  return false;
441  else {
442  client->setParent(qApp);
443  RestClientPrivate::globalApis.insert(name, client);
444  return true;
445  }
446 }
447 
462 void QtRestClient::removeGlobalApi(const QString &name, bool deleteClient)
463 {
464  QWriteLocker _{&RestClientPrivate::globalApiLock};
465  if (deleteClient) {
466  auto client = RestClientPrivate::globalApis.take(name);
467  if (client)
468  client->deleteLater();
469  } else
470  RestClientPrivate::globalApis.remove(name);
471 }
472 
480 {
481  QReadLocker _{&RestClientPrivate::globalApiLock};
482  return RestClientPrivate::globalApis.value(name, nullptr);
483 }
484 
492 {
493  QReadLocker _{&RestClientPrivate::globalApiLock};
494  auto client = RestClientPrivate::globalApis.value(name, nullptr);
495  if (client)
496  return client->rootClass();
497  else
498  return nullptr;
499 }
500 
509 RestClass *QtRestClient::createApiClass(const QString &name, const QString &path, QObject *parent)
510 {
511  QReadLocker _{&RestClientPrivate::globalApiLock};
512  auto client = RestClientPrivate::globalApis.value(name, nullptr);
513  if (client)
514  return client->createClass(path, parent);
515  else
516  return nullptr;
517 }
QtRestClient::RestClient::setBaseUrl
void setBaseUrl(QUrl baseUrl)
WRITE accessor for RestClient::baseUrl.
Definition: restclient.cpp:235
QHash::value
const T value(const Key &key) const const
QtRestClient::RestClient::createClass
RestClass * createClass(const QString &path, QObject *parent=nullptr)
Creates a new rest class for the given path and parent.
Definition: restclient.cpp:38
QString::split
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QtRestClient::apiClient
Q_RESTCLIENT_EXPORT RestClient * apiClient(const QString &name)
Returns the client for given API name.
Definition: restclient.cpp:479
QtRestClient::RequestBuilder::addHeaders
RequestBuilder & addHeaders(const HeaderHash &headers)
Adds HTTP headers to be added to the network request.
QtRestClient::RestClient::pagingFactory
IPagingFactory * pagingFactory() const
Returns the paging factory used by the restclient.
Definition: restclient.cpp:65
QtRestClient::RequestBuilder::setSslConfig
RequestBuilder & setSslConfig(QSslConfiguration sslConfig)
Sets the ssl configuration to be used by the network request.
QtRestClient::RestClient::threadedChanged
void threadedChanged(bool threaded, QPrivateSignal)
NOTIFY accessor for RestClient::threaded.
QtRestClient
The Namespace containing all classes of the QtRestClient module.
Definition: genericrestreply.h:14
QUrl
QObject::Q_EMIT
Q_EMITQ_EMIT
QtRestClient::RestClient::serializer
QtJsonSerializer::SerializerBase * serializer() const
Returns the json serializer used by the restclient.
Definition: restclient.cpp:57
QtRestClient::RestClient::setGlobalHeaders
void setGlobalHeaders(HeaderHash globalHeaders)
WRITE accessor for RestClient::globalHeaders.
Definition: restclient.cpp:257
QtJsonSerializer::JsonSerializer
QtRestClient::RestClient::asyncPoolChanged
void asyncPoolChanged(QThreadPool *asyncPool, QPrivateSignal)
NOTIFY accessor for RestClient::asyncPool.
QtRestClient::RestClient::setPagingFactory
void setPagingFactory(IPagingFactory *factory)
Sets the paging factory to be used by all paging requests for this client.
Definition: restclient.cpp:198
QtJsonSerializer::SerializerBase
QtRestClient::RequestBuilder::setAttributes
RequestBuilder & setAttributes(const QHash< QNetworkRequest::Attribute, QVariant > &attributes)
Sets the given attributes on the generated network request.
QtRestClient::RestClient::apiVersionChanged
void apiVersionChanged(QVersionNumber apiVersion, QPrivateSignal)
NOTIFY accessor for RestClient::apiVersion.
QtRestClient::RestClient::DataMode::Cbor
The client expects and sends data in the binary CBOR format.
QHash::insert
QHash::iterator insert(const Key &key, const T &value)
QtRestClient::RestClient::setAsyncPool
void setAsyncPool(QThreadPool *asyncPool)
WRITE accessor for RestClient::asyncPool.
Definition: restclient.cpp:329
QtRestClient::RestClient::setSerializer
void setSerializer(QtJsonSerializer::SerializerBase *serializer)
Sets the json serializer to be used by all requests for this client.
Definition: restclient.cpp:182
QtRestClient::RestClass
A class to perform requests to an API.
Definition: restclass.h:19
QtRestClient::RestClient::setDataMode
void setDataMode(DataMode dataMode)
WRITE accessor for RestClient::dataMode.
Definition: restclient.cpp:205
QtRestClient::RestClient::baseUrl
QUrl baseUrl() const
READ accessor for RestClient::baseUrl.
QtRestClient::RequestBuilder::addParameters
RequestBuilder & addParameters(const QUrlQuery &parameters)
Adds parameters to the URL.
QReadLocker
QtRestClient::addGlobalApi
Q_RESTCLIENT_EXPORT bool addGlobalApi(const QString &name, RestClient *client)
Makes the given API available under the given name.
Definition: restclient.cpp:436
QHash::take
T take(const Key &key)
QtRestClient::RequestBuilder
A helper class to build QUrl and QNetworkRequest objects.
Definition: requestbuilder.h:24
QtJsonSerializer::SerializerBase::setAllowDefaultNull
void setAllowDefaultNull(bool allowDefaultNull)
QtRestClient::RestClient::manager
QNetworkAccessManager * manager() const
Returns the network access manager used by the restclient.
Definition: restclient.cpp:49
QString
QtRestClient::RestClient::removeGlobalParameter
void removeGlobalParameter(const QString &name)
WRITE accessor for RestClient::globalParameters.
Definition: restclient.cpp:367
QtRestClient::removeGlobalApi
Q_RESTCLIENT_EXPORT void removeGlobalApi(const QString &name, bool deleteClient=true)
Removes a previously added API from the global list.
Definition: restclient.cpp:462
QtRestClient::RestClient::asyncPool
QThreadPool * asyncPool() const
READ accessor for RestClient::asyncPool.
QtRestClient::RestClient::setGlobalParameters
void setGlobalParameters(QUrlQuery globalParameters)
WRITE accessor for RestClient::globalParameters.
Definition: restclient.cpp:268
QtRestClient::RestClient::addRequestAttribute
void addRequestAttribute(QNetworkRequest::Attribute attribute, const QVariant &value)
WRITE accessor for RestClient::requestAttributes.
Definition: restclient.cpp:375
QThreadPool
QtRestClient::createApiClass
Q_RESTCLIENT_EXPORT RestClass * createApiClass(const QString &name, const QString &path, QObject *parent=nullptr)
Creates a new API class based on the client for the given API name.
Definition: restclient.cpp:509
QtRestClient::RestClient::globalHeaders
HeaderHash globalHeaders() const
READ accessor for RestClient::globalHeaders.
QtRestClient::RequestBuilder::setAccept
RequestBuilder & setAccept(const QByteArray &mimeType)
Sets the "Accept" HTTP-header to the given mimetype.
QNetworkAccessManager::setRedirectPolicy
void setRedirectPolicy(QNetworkRequest::RedirectPolicy policy)
QtRestClient::RestClient::requestAttributesChanged
void requestAttributesChanged(QHash< QNetworkRequest::Attribute, QVariant > requestAttributes, QPrivateSignal)
NOTIFY accessor for RestClient::requestAttributes.
QVersionNumber
QtRestClient::RestClient::rootClass
RestClass * rootClass() const
Returns the rest class with the root path.
Definition: restclient.cpp:43
QtRestClient::RestClient::globalHeadersChanged
void globalHeadersChanged(HeaderHash globalHeaders, QPrivateSignal)
NOTIFY accessor for RestClient::globalHeaders.
QtRestClient::RestClient::threaded
bool threaded
Specifies, whether the client can be used in a multithreaded context.
Definition: restclient.h:48
QtRestClient::RestClient::setSslConfiguration
void setSslConfiguration(QSslConfiguration sslConfiguration)
WRITE accessor for RestClient::sslConfiguration.
Definition: restclient.cpp:316
QtJsonSerializer::CborSerializer
restclass.h
QtRestClient::RestClient::requestAttributes
QHash< QNetworkRequest::Attribute, QVariant > requestAttributes() const
READ accessor for RestClient::requestAttributes.
QtRestClient::RestClient::builder
virtual RequestBuilder builder() const
Creates a request builder with all the settings of this client.
Definition: restclient.cpp:144
QtRestClient::RestClient::setModernAttributes
void setModernAttributes()
Sets modern attributes in RestClient::requestAttributes.
Definition: restclient.cpp:290
QtRestClient::RestClient::isThreaded
bool isThreaded() const
READ accessor for RestClient::threaded.
Definition: restclient.cpp:120
QtRestClient::RestClient::setApiVersion
void setApiVersion(QVersionNumber apiVersion)
WRITE accessor for RestClient::apiVersion.
Definition: restclient.cpp:246
QtRestClient::RestClient::setRequestAttributes
void setRequestAttributes(QHash< QNetworkRequest::Attribute, QVariant > requestAttributes)
WRITE accessor for RestClient::requestAttributes.
Definition: restclient.cpp:279
QtRestClient::RestClient::globalParametersChanged
void globalParametersChanged(QUrlQuery globalParameters, QPrivateSignal)
NOTIFY accessor for RestClient::globalParameters.
QLatin1Char
QHash::remove
int remove(const Key &key)
QtRestClient::RestClient::DataMode::Json
The client expects and sends data in the textual JSON format.
QObject::setParent
void setParent(QObject *parent)
QtRestClient::RestClient::sslConfiguration
QSslConfiguration sslConfiguration() const
READ accessor for RestClient::sslConfiguration.
QWriteLocker
QtRestClient::RestClient::setThreaded
void setThreaded(bool threaded)
WRITE accessor for RestClient::threaded.
Definition: restclient.cpp:300
QtRestClient::RequestBuilder::setVersion
RequestBuilder & setVersion(QVersionNumber version)
Sets the version of the API.
QSslConfiguration
QtRestClient::apiRootClass
Q_RESTCLIENT_EXPORT RestClass * apiRootClass(const QString &name)
Returns the clients root class for the given API name.
Definition: restclient.cpp:491
QtRestClient::RestClient::globalParameters
QUrlQuery globalParameters() const
READ accessor for RestClient::globalParameters.
QtRestClient::RestClient::RestClient
RestClient(QObject *parent=nullptr)
Constructor.
Definition: restclient.cpp:18
QtRestClient::RestClient::baseUrlChanged
void baseUrlChanged(QUrl baseUrl, QPrivateSignal)
NOTIFY accessor for RestClient::baseUrl.
QVariant
QObject
QReadWriteLock
QtRestClient::RestClient::DataMode
DataMode
The different data modes in which the client can operate.
Definition: restclient.h:62
QtRestClient::RestClient
A class to define access to an API, with general settings.
Definition: restclient.h:30
QNetworkAccessManager
QHash
QtRestClient::RestClient::apiVersion
QVersionNumber apiVersion() const
READ accessor for RestClient::apiVersion.
QtRestClient::RestClient::setManager
void setManager(QNetworkAccessManager *manager)
Sets the network access manager to be used by all requests for this client.
Definition: restclient.cpp:172
QtRestClient::RestClient::removeGlobalHeader
void removeGlobalHeader(const QByteArray &name)
WRITE accessor for RestClient::globalHeaders.
Definition: restclient.cpp:351
QtRestClient::RestClient::dataModeChanged
void dataModeChanged(DataMode dataMode, QPrivateSignal)
NOTIFY accessor for RestClient::dataMode.
QObject::parent
QObject * parent() const const
QtRestClient::RestClient::sslConfigurationChanged
void sslConfigurationChanged(QSslConfiguration sslConfiguration, QPrivateSignal)
NOTIFY accessor for RestClient::sslConfiguration.
QtRestClient::RestClient::removeRequestAttribute
void removeRequestAttribute(QNetworkRequest::Attribute attribute)
WRITE accessor for RestClient::requestAttributes.
Definition: restclient.cpp:383
QByteArray
QtRestClient::RestClient::addGlobalHeader
void addGlobalHeader(const QByteArray &name, const QByteArray &value)
WRITE accessor for RestClient::globalHeaders.
Definition: restclient.cpp:343
QtRestClient::RestClient::dataMode
DataMode dataMode() const
READ accessor for RestClient::dataMode.
QUrlQuery
QtRestClient::IPagingFactory
A factory interface to create IPaging instances from raw data.
Definition: ipaging.h:77