From 8da4156da7ed2eff7b44bdda51eb92870bdfbc57 Mon Sep 17 00:00:00 2001 From: Juha Vuolle Date: Mon, 20 Nov 2023 12:56:10 +0200 Subject: [PATCH] Add PATCH support for QRestAccessManager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Ivan Solovev Reviewed-by: MÃ¥rten Nordheim --- src/network/access/qrestaccessmanager.cpp | 125 ++++++++++++++++++ src/network/access/qrestaccessmanager.h | 5 + .../src_network_access_qrestaccessmanager.cpp | 8 ++ .../qrestaccessmanager/httptestserver.cpp | 2 + .../qrestaccessmanager/httptestserver_p.h | 1 + .../tst_qrestaccessmanager.cpp | 25 ++++ 6 files changed, 166 insertions(+) diff --git a/src/network/access/qrestaccessmanager.cpp b/src/network/access/qrestaccessmanager.cpp index 2a2c1247d88..146d8b24b93 100644 --- a/src/network/access/qrestaccessmanager.cpp +++ b/src/network/access/qrestaccessmanager.cpp @@ -108,6 +108,7 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest") \li \c post() \li \c put() \li \c head() + \li \c patch() \li \c deleteResource() \li \c sendCustomRequest() \row @@ -116,6 +117,7 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest") \li - \li - \li X + \li - \li X \li - \row @@ -124,6 +126,7 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest") \li X \li X \li - + \li X \li - \li X \row @@ -132,6 +135,7 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest") \li X \li X \li - + \li X \li - \li - \row @@ -140,6 +144,7 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest") \li X \li X \li - + \li X \li - \li - \row @@ -148,6 +153,7 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest") \li X \li X \li - + \li X \li - \li - \row @@ -157,6 +163,7 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest") \li X \li - \li - + \li - \li X \row \li QIODevice @@ -164,6 +171,7 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest") \li X \li X \li - + \li X \li - \li X \endtable @@ -438,6 +446,77 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest") \overload */ +/*! + \fn template> QRestReply *QRestAccessManager::patch( + const QNetworkRequest &request, const QJsonObject &data, + const ContextTypeForFunctor *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> QRestReply *QRestAccessManager::patch( + const QNetworkRequest &request, const QJsonArray &data, + const ContextTypeForFunctor *context, + Functor &&callback) + + \overload +*/ + +/*! + \fn template> QRestReply *QRestAccessManager::patch( + const QNetworkRequest &request, const QVariantMap &data, + const ContextTypeForFunctor *context, + Functor &&callback) + + \overload +*/ + +/*! + \fn template> QRestReply *QRestAccessManager::patch( + const QNetworkRequest &request, const QByteArray &data, + const ContextTypeForFunctor *context, + Functor &&callback) + + \overload +*/ + +/*! + \fn template> QRestReply *QRestAccessManager::patch( + const QNetworkRequest &request, QIODevice *data, + const ContextTypeForFunctor *context, + Functor &&callback) + + \overload +*/ + /*! \fn template> QRestReply *QRestAccessManager::head( const QNetworkRequest &request, @@ -800,6 +879,52 @@ QRestReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request, 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, const QByteArray& method, const QByteArray &data, const QObject *context, diff --git a/src/network/access/qrestaccessmanager.h b/src/network/access/qrestaccessmanager.h index 3ff23bee905..9656853f43d 100644 --- a/src/network/access/qrestaccessmanager.h +++ b/src/network/access/qrestaccessmanager.h @@ -114,6 +114,11 @@ public: QREST_METHOD_WITH_DATA(put, const QByteArray &) QREST_METHOD_WITH_DATA(put, QHttpMultiPart *) 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(QIODevice *) QREST_METHOD_CUSTOM_WITH_DATA(QHttpMultiPart *) diff --git a/src/network/doc/snippets/code/src_network_access_qrestaccessmanager.cpp b/src/network/doc/snippets/code/src_network_access_qrestaccessmanager.cpp index 20bb5d521d0..fcc8dbaef52 100644 --- a/src/network/doc/snippets/code/src_network_access_qrestaccessmanager.cpp +++ b/src/network/doc/snippets/code/src_network_access_qrestaccessmanager.cpp @@ -89,3 +89,11 @@ manager->sendCustomRequest(request, "MYMETHOD", myData, this, [this](QRestRepl // ... }); //! [9] + + +//! [10] +manager->patch(request, myData, this, [this](QRestReply *reply) { + if (reply->isSuccess()) + // ... +}); +//! [10] diff --git a/tests/auto/network/access/qrestaccessmanager/httptestserver.cpp b/tests/auto/network/access/qrestaccessmanager/httptestserver.cpp index dfa2b45063d..089342ff8bf 100644 --- a/tests/auto/network/access/qrestaccessmanager/httptestserver.cpp +++ b/tests/auto/network/access/qrestaccessmanager/httptestserver.cpp @@ -147,6 +147,8 @@ bool HttpTestServer::readMethod(QTcpSocket *socket) method = Method::Get; else if (fragment == "PUT") method = Method::Put; + else if (fragment == "PATCH") + method = Method::Patch; else if (fragment == "POST") method = Method::Post; else if (fragment == "DELETE") diff --git a/tests/auto/network/access/qrestaccessmanager/httptestserver_p.h b/tests/auto/network/access/qrestaccessmanager/httptestserver_p.h index d1819fcc21a..1498c4bdb7e 100644 --- a/tests/auto/network/access/qrestaccessmanager/httptestserver_p.h +++ b/tests/auto/network/access/qrestaccessmanager/httptestserver_p.h @@ -54,6 +54,7 @@ public: Head, Get, Put, + Patch, Post, Delete, Custom, diff --git a/tests/auto/network/access/qrestaccessmanager/tst_qrestaccessmanager.cpp b/tests/auto/network/access/qrestaccessmanager/tst_qrestaccessmanager.cpp index f66968bf1cf..b2cfcf4a754 100644 --- a/tests/auto/network/access/qrestaccessmanager/tst_qrestaccessmanager.cpp +++ b/tests/auto/network/access/qrestaccessmanager/tst_qrestaccessmanager.cpp @@ -108,6 +108,7 @@ void tst_QRestAccessManager::networkRequestReply() const QByteArray methodPOST{"POST"_ba}; const QByteArray methodGET{"GET"_ba}; const QByteArray methodPUT{"PUT"_ba}; + const QByteArray methodPATCH{"PATCH"_ba}; const QByteArray methodCUSTOM{"FOOBAR"_ba}; // DELETE @@ -209,6 +210,27 @@ void tst_QRestAccessManager::networkRequestReply() VERIFY_REPLY_OK(methodPUT); 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 //manager.get(request, [](){}); // 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.put(request, QString()); // wrong datatype //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.head(request, "f"_ba); // data not allowed //manager.post(request, ""_ba, this, [](int param){}); // Wrong callback signature