Add PATCH support for QRestAccessManager

It is somewhat common HTTP method with RESTful use cases
(partial updates on resources)

Task-number: QTBUG-114637
Change-Id: Id252d3f4b54c3ebb8df5c93259e64a4af2d0ca2f
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Juha Vuolle 2023-11-20 12:56:10 +02:00
parent a80ed49b10
commit 8da4156da7
6 changed files with 166 additions and 0 deletions

View File

@ -108,6 +108,7 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest")
\li \c post() \li \c post()
\li \c put() \li \c put()
\li \c head() \li \c head()
\li \c patch()
\li \c deleteResource() \li \c deleteResource()
\li \c sendCustomRequest() \li \c sendCustomRequest()
\row \row
@ -116,6 +117,7 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest")
\li - \li -
\li - \li -
\li X \li X
\li -
\li X \li X
\li - \li -
\row \row
@ -124,6 +126,7 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest")
\li X \li X
\li X \li X
\li - \li -
\li X
\li - \li -
\li X \li X
\row \row
@ -132,6 +135,7 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest")
\li X \li X
\li X \li X
\li - \li -
\li X
\li - \li -
\li - \li -
\row \row
@ -140,6 +144,7 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest")
\li X \li X
\li X \li X
\li - \li -
\li X
\li - \li -
\li - \li -
\row \row
@ -148,6 +153,7 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest")
\li X \li X
\li X \li X
\li - \li -
\li X
\li - \li -
\li - \li -
\row \row
@ -157,6 +163,7 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest")
\li X \li X
\li - \li -
\li - \li -
\li -
\li X \li X
\row \row
\li QIODevice \li QIODevice
@ -164,6 +171,7 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest")
\li X \li X
\li X \li X
\li - \li -
\li X
\li - \li -
\li X \li X
\endtable \endtable
@ -438,6 +446,77 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest")
\overload \overload
*/ */
/*!
\fn template<typename Functor, if_compatible_callback<Functor>> QRestReply *QRestAccessManager::patch(
const QNetworkRequest &request, const QJsonObject &data,
const ContextTypeForFunctor<Functor> *context,
Functor &&callback)
Issues an \c {HTTP PATCH} based on \a request.
The optional \a callback and \a context object can be provided for
handling the request completion as illustrated below:
\snippet code/src_network_access_qrestaccessmanager.cpp 10
Alternatively the signals of the returned QRestReply* object can be
used. For further information see
\l {Issuing Network Requests and Handling Replies}.
The \c patch() method always requires \a data parameter. The following
data types are supported:
\list
\li QByteArray
\li QJsonObject *)
\li QJsonArray *)
\li QVariantMap **)
\li QIODevice*
\endlist
*) Sent in \l QJsonDocument::Compact format, and the
\c Content-Type header is set to \c {application/json} if the
\c Content-Type header was not set
**) QVariantMap is converted to and treated as a QJsonObject
\sa QRestReply, QRestReply::finished(), QRestAccessManager::requestFinished()
*/
/*!
\fn template<typename Functor, if_compatible_callback<Functor>> QRestReply *QRestAccessManager::patch(
const QNetworkRequest &request, const QJsonArray &data,
const ContextTypeForFunctor<Functor> *context,
Functor &&callback)
\overload
*/
/*!
\fn template<typename Functor, if_compatible_callback<Functor>> QRestReply *QRestAccessManager::patch(
const QNetworkRequest &request, const QVariantMap &data,
const ContextTypeForFunctor<Functor> *context,
Functor &&callback)
\overload
*/
/*!
\fn template<typename Functor, if_compatible_callback<Functor>> QRestReply *QRestAccessManager::patch(
const QNetworkRequest &request, const QByteArray &data,
const ContextTypeForFunctor<Functor> *context,
Functor &&callback)
\overload
*/
/*!
\fn template<typename Functor, if_compatible_callback<Functor>> QRestReply *QRestAccessManager::patch(
const QNetworkRequest &request, QIODevice *data,
const ContextTypeForFunctor<Functor> *context,
Functor &&callback)
\overload
*/
/*! /*!
\fn template<typename Functor, if_compatible_callback<Functor>> QRestReply *QRestAccessManager::head( \fn template<typename Functor, if_compatible_callback<Functor>> QRestReply *QRestAccessManager::head(
const QNetworkRequest &request, const QNetworkRequest &request,
@ -800,6 +879,52 @@ QRestReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
return d->executeRequest([&]() { return d->qnam->put(request, data); }, context, slot); return d->executeRequest([&]() { return d->qnam->put(request, data); }, context, slot);
} }
static const auto PATCH = "PATCH"_ba;
QRestReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request,
const QJsonObject &data, const QObject *context,
QtPrivate::QSlotObjectBase *slot)
{
Q_D(QRestAccessManager);
return d->executeRequest(
[&](auto req, auto json){ return d->qnam->sendCustomRequest(req, PATCH, json); },
data, request, context, slot);
}
QRestReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request,
const QJsonArray &data, const QObject *context,
QtPrivate::QSlotObjectBase *slot)
{
Q_D(QRestAccessManager);
return d->executeRequest(
[&](auto req, auto json){ return d->qnam->sendCustomRequest(req, PATCH, json); },
data, request, context, slot);
}
QRestReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request,
const QVariantMap &data, const QObject *context,
QtPrivate::QSlotObjectBase *slot)
{
return patchWithDataImpl(request, QJsonObject::fromVariantMap(data), context, slot);
}
QRestReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request,
const QByteArray &data, const QObject *context,
QtPrivate::QSlotObjectBase *slot)
{
Q_D(QRestAccessManager);
return d->executeRequest([&]() { return d->qnam->sendCustomRequest(request, PATCH, data); },
context, slot);
}
QRestReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request, QIODevice *data,
const QObject *context, QtPrivate::QSlotObjectBase *slot)
{
Q_D(QRestAccessManager);
return d->executeRequest([&]() { return d->qnam->sendCustomRequest(request, PATCH, data); },
context, slot);
}
QRestReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request, QRestReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request,
const QByteArray& method, const QByteArray &data, const QByteArray& method, const QByteArray &data,
const QObject *context, const QObject *context,

View File

@ -114,6 +114,11 @@ public:
QREST_METHOD_WITH_DATA(put, const QByteArray &) QREST_METHOD_WITH_DATA(put, const QByteArray &)
QREST_METHOD_WITH_DATA(put, QHttpMultiPart *) QREST_METHOD_WITH_DATA(put, QHttpMultiPart *)
QREST_METHOD_WITH_DATA(put, QIODevice *) QREST_METHOD_WITH_DATA(put, QIODevice *)
QREST_METHOD_WITH_DATA(patch, const QJsonObject &)
QREST_METHOD_WITH_DATA(patch, const QJsonArray &)
QREST_METHOD_WITH_DATA(patch, const QVariantMap &)
QREST_METHOD_WITH_DATA(patch, const QByteArray &)
QREST_METHOD_WITH_DATA(patch, QIODevice *)
QREST_METHOD_CUSTOM_WITH_DATA(const QByteArray &) QREST_METHOD_CUSTOM_WITH_DATA(const QByteArray &)
QREST_METHOD_CUSTOM_WITH_DATA(QIODevice *) QREST_METHOD_CUSTOM_WITH_DATA(QIODevice *)
QREST_METHOD_CUSTOM_WITH_DATA(QHttpMultiPart *) QREST_METHOD_CUSTOM_WITH_DATA(QHttpMultiPart *)

View File

@ -89,3 +89,11 @@ manager->sendCustomRequest(request, "MYMETHOD", myData, this, [this](QRestRepl
// ... // ...
}); });
//! [9] //! [9]
//! [10]
manager->patch(request, myData, this, [this](QRestReply *reply) {
if (reply->isSuccess())
// ...
});
//! [10]

View File

@ -147,6 +147,8 @@ bool HttpTestServer::readMethod(QTcpSocket *socket)
method = Method::Get; method = Method::Get;
else if (fragment == "PUT") else if (fragment == "PUT")
method = Method::Put; method = Method::Put;
else if (fragment == "PATCH")
method = Method::Patch;
else if (fragment == "POST") else if (fragment == "POST")
method = Method::Post; method = Method::Post;
else if (fragment == "DELETE") else if (fragment == "DELETE")

View File

@ -54,6 +54,7 @@ public:
Head, Head,
Get, Get,
Put, Put,
Patch,
Post, Post,
Delete, Delete,
Custom, Custom,

View File

@ -108,6 +108,7 @@ void tst_QRestAccessManager::networkRequestReply()
const QByteArray methodPOST{"POST"_ba}; const QByteArray methodPOST{"POST"_ba};
const QByteArray methodGET{"GET"_ba}; const QByteArray methodGET{"GET"_ba};
const QByteArray methodPUT{"PUT"_ba}; const QByteArray methodPUT{"PUT"_ba};
const QByteArray methodPATCH{"PATCH"_ba};
const QByteArray methodCUSTOM{"FOOBAR"_ba}; const QByteArray methodCUSTOM{"FOOBAR"_ba};
// DELETE // DELETE
@ -209,6 +210,27 @@ void tst_QRestAccessManager::networkRequestReply()
VERIFY_REPLY_OK(methodPUT); VERIFY_REPLY_OK(methodPUT);
QCOMPARE(serverSideRequest.body, ioDeviceData); QCOMPARE(serverSideRequest.body, ioDeviceData);
// PATCH
manager.patch(request, byteArrayData, this, callback);
VERIFY_REPLY_OK(methodPATCH);
QCOMPARE(serverSideRequest.body, byteArrayData);
manager.patch(request, jsonObjectData, this, callback);
VERIFY_REPLY_OK(methodPATCH);
QCOMPARE(QJsonDocument::fromJson(serverSideRequest.body).object(), jsonObjectData);
manager.patch(request, jsonArrayData, this, callback);
VERIFY_REPLY_OK(methodPATCH);
QCOMPARE(QJsonDocument::fromJson(serverSideRequest.body).array(), jsonArrayData);
manager.patch(request, variantMapData, this, callback);
VERIFY_REPLY_OK(methodPATCH);
QCOMPARE(QJsonDocument::fromJson(serverSideRequest.body).object(), jsonObjectData);
manager.patch(request, &bufferIoDevice, this, callback);
VERIFY_REPLY_OK(methodPATCH);
QCOMPARE(serverSideRequest.body, ioDeviceData);
//These must NOT compile //These must NOT compile
//manager.get(request, [](){}); // callback without context object //manager.get(request, [](){}); // callback without context object
//manager.get(request, ""_ba, [](){}); // callback without context object //manager.get(request, ""_ba, [](){}); // callback without context object
@ -219,6 +241,9 @@ void tst_QRestAccessManager::networkRequestReply()
//manager.post(request); // data is required //manager.post(request); // data is required
//manager.put(request, QString()); // wrong datatype //manager.put(request, QString()); // wrong datatype
//manager.put(request); // data is required //manager.put(request); // data is required
//manager.patch(request, 123); // wrong datatype
//manager.patch(request, QString()); // wrong datatype
//manager.patch(request); // data is required
//manager.deleteResource(request, "f"_ba); // data not allowed //manager.deleteResource(request, "f"_ba); // data not allowed
//manager.head(request, "f"_ba); // data not allowed //manager.head(request, "f"_ba); // data not allowed
//manager.post(request, ""_ba, this, [](int param){}); // Wrong callback signature //manager.post(request, ""_ba, this, [](int param){}); // Wrong callback signature