Skip to content

Commit 04e0ae8

Browse files
committed
Add support for complex network requests
1 parent 0acc980 commit 04e0ae8

10 files changed

+423
-184
lines changed

docs/scripting-api.rst

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2094,6 +2094,45 @@ Types
20942094

20952095
Exit code
20962096

2097+
.. js:class:: NetworkRequest
2098+
2099+
Make HTTP requests.
2100+
2101+
Example:
2102+
2103+
.. code-block:: js
2104+
2105+
let req = NetworkRequest();
2106+
2107+
# allow redirects
2108+
req.maxRedirects = 5;
2109+
2110+
# set request headers
2111+
req.headers = {
2112+
'User-Agent': req.headers['User-Agent'],
2113+
'Accept': 'application/json',
2114+
};
2115+
2116+
# create JSON data
2117+
const data = JSON.stringify({text: 'Hello, **world**!'});
2118+
2119+
# send POST request
2120+
const reply = req.request(
2121+
'POST', 'https://api.github.com/markdown', data)
2122+
2123+
# the request is synchronous and may not be finished
2124+
# until a property is called (like reply.data or reply.status)
2125+
if (!reply.finished) { serverLog('Processing...'); }
2126+
print(reply.data);
2127+
2128+
.. js:attribute:: headers
2129+
2130+
Object with HTTP headers
2131+
2132+
.. js:attribute:: maxRedirects
2133+
2134+
Maximum number of redirects to follow (default is 0)
2135+
20972136
.. js:class:: NetworkReply
20982137

20992138
Received network reply object.
@@ -2123,6 +2162,11 @@ Types
21232162
True only if request has been completed, false only for unfinished
21242163
asynchronous requests
21252164

2165+
.. js:attribute:: url
2166+
2167+
URL of the final request (may be different from the original if
2168+
redirects are enabled)
2169+
21262170
.. js:class:: Command
21272171

21282172
Wrapper for a command (from Command dialog).

src/gui/commandcompleterdocumentation.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ void addDocumentation(AddDocumentationCallback addDocumentation)
190190
addDocumentation("Item", "Item", "Object with MIME types of an item.");
191191
addDocumentation("ItemSelection", "ItemSelection", "List of items from given tab.");
192192
addDocumentation("FinishedCommand", "FinishedCommand", "Properties of finished command.");
193+
addDocumentation("NetworkRequest", "NetworkRequest", "Make HTTP requests.");
193194
addDocumentation("NetworkReply", "NetworkReply", "Received network reply object.");
194195
addDocumentation("Command", "Command", "Wrapper for a command (from Command dialog).");
195196
addDocumentation("arguments", "arguments", "Array for accessing arguments passed to current function or the script (`arguments[0]` is the script itself).");

src/scriptable/scriptable.cpp

Lines changed: 30 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include "scriptable/scriptabletemporaryfile.h"
3131
#include "scriptable/scriptoverrides.h"
3232
#include "scriptable/scriptvaluefactory.h"
33+
#include "scriptable/scriptablenetworkrequest.h"
34+
#include "scriptable/scriptablenetworkreply.h"
3335

3436
#include <QApplication>
3537
#include <QCryptographicHash>
@@ -428,6 +430,10 @@ Scriptable::Scriptable(
428430
&ScriptableItemSelection::staticMetaObject, QStringLiteral("ItemSelection"), m_engine);
429431
m_settingsPrototype = addScriptableClass(
430432
&ScriptableSettings::staticMetaObject, QStringLiteral("Settings"), m_engine);
433+
m_networkRequestPrototype = addScriptableClass(
434+
&ScriptableNetworkRequest::staticMetaObject, QStringLiteral("NetworkRequest"), m_engine);
435+
m_networkReplyPrototype = addScriptableClass(
436+
&ScriptableNetworkReply::staticMetaObject, QStringLiteral("NetworkReply"), m_engine);
431437
}
432438

433439
QJSValue Scriptable::argumentsArray() const
@@ -713,6 +719,16 @@ QJSValue Scriptable::Settings() const
713719
return newQObject(new ScriptableSettings(toString(arg)), m_settingsPrototype);
714720
}
715721

722+
QJSValue Scriptable::NetworkReply() const
723+
{
724+
return newQObject(new ScriptableNetworkReply(), m_networkReplyPrototype);
725+
}
726+
727+
QJSValue Scriptable::NetworkRequest() const
728+
{
729+
return newQObject(new ScriptableNetworkRequest(), m_networkRequestPrototype);
730+
}
731+
716732
QJSValue Scriptable::version()
717733
{
718734
m_skipArguments = 0;
@@ -2158,28 +2174,32 @@ QJSValue Scriptable::exportCommands()
21582174

21592175
QJSValue Scriptable::networkGet()
21602176
{
2161-
NetworkReply *reply = networkGetHelper();
2162-
reply->data();
2163-
return reply->toScriptValue();
2177+
QJSValue reply = networkGetAsync();
2178+
reply.property("data");
2179+
return reply;
21642180
}
21652181

21662182
QJSValue Scriptable::networkPost()
21672183
{
2168-
NetworkReply *reply = networkPostHelper();
2169-
reply->data();
2170-
return reply->toScriptValue();
2184+
QJSValue reply = networkPostAsync();
2185+
reply.property("data");
2186+
return reply;
21712187
}
21722188

21732189
QJSValue Scriptable::networkGetAsync()
21742190
{
2175-
NetworkReply *reply = networkGetHelper();
2176-
return reply->toScriptValue();
2191+
m_skipArguments = 1;
2192+
auto requestJs = this->NetworkRequest();
2193+
auto request = qobject_cast<ScriptableNetworkRequest *>(requestJs.toQObject());
2194+
return request->request(newByteArray("GET"), argument(0), {});
21772195
}
21782196

21792197
QJSValue Scriptable::networkPostAsync()
21802198
{
2181-
NetworkReply *reply = networkPostHelper();
2182-
return reply->toScriptValue();
2199+
m_skipArguments = 2;
2200+
auto requestJs = this->NetworkRequest();
2201+
auto request = qobject_cast<ScriptableNetworkRequest *>(requestJs.toQObject());
2202+
return request->request(newByteArray("POST"), argument(0), argument(1));
21832203
}
21842204

21852205
QJSValue Scriptable::env()
@@ -3651,21 +3671,6 @@ void Scriptable::interruptibleSleep(int msec)
36513671
loop.exec();
36523672
}
36533673

3654-
NetworkReply *Scriptable::networkGetHelper()
3655-
{
3656-
m_skipArguments = 1;
3657-
const QString url = arg(0);
3658-
return NetworkReply::get(url, this);
3659-
}
3660-
3661-
NetworkReply *Scriptable::networkPostHelper()
3662-
{
3663-
m_skipArguments = 2;
3664-
const QString url = arg(0);
3665-
const QByteArray postData = makeByteArray(argument(1));
3666-
return NetworkReply::post(url, postData, this);
3667-
}
3668-
36693674
QJSValue Scriptable::newQObject(QObject *obj, const QJSValue &prototype) const
36703675
{
36713676
auto value = m_engine->newQObject(obj);
@@ -3715,120 +3720,6 @@ void Scriptable::installObject(QObject *fromObj, const QMetaObject *metaObject,
37153720
}
37163721
}
37173722

3718-
NetworkReply *NetworkReply::get(const QString &url, Scriptable *scriptable)
3719-
{
3720-
return new NetworkReply(url, QByteArray(), scriptable);
3721-
}
3722-
3723-
NetworkReply *NetworkReply::post(const QString &url, const QByteArray &postData, Scriptable *scriptable)
3724-
{
3725-
return new NetworkReply(url, postData, scriptable);
3726-
}
3727-
3728-
NetworkReply::~NetworkReply()
3729-
{
3730-
if (m_reply)
3731-
m_reply->deleteLater();
3732-
}
3733-
3734-
QJSValue NetworkReply::data()
3735-
{
3736-
if ( !m_data.isUndefined() )
3737-
return m_data;
3738-
3739-
if ( !m_reply->isFinished() && m_scriptable->canContinue() ) {
3740-
QEventLoop loop;
3741-
connect(m_scriptable, &Scriptable::finished, &loop, &QEventLoop::quit);
3742-
connect(m_reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
3743-
loop.exec();
3744-
}
3745-
3746-
if ( !m_reply->isFinished() )
3747-
return QJSValue();
3748-
3749-
m_data = m_scriptable->newByteArray(m_rawData);
3750-
3751-
return m_data;
3752-
}
3753-
3754-
QJSValue NetworkReply::error()
3755-
{
3756-
data();
3757-
3758-
if (m_reply->error() != QNetworkReply::NoError)
3759-
return m_reply->errorString();
3760-
3761-
return QJSValue();
3762-
}
3763-
3764-
QJSValue NetworkReply::status()
3765-
{
3766-
data();
3767-
3768-
const QVariant v = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
3769-
if (v.isValid())
3770-
return v.toInt();
3771-
return QJSValue();
3772-
}
3773-
3774-
QJSValue NetworkReply::redirect()
3775-
{
3776-
data();
3777-
3778-
const QVariant v = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
3779-
if (v.isValid())
3780-
return v.toUrl().resolved(m_reply->url()).toString();
3781-
return QJSValue();
3782-
}
3783-
3784-
QJSValue NetworkReply::headers()
3785-
{
3786-
data();
3787-
3788-
QJSValue headers = m_scriptable->engine()->newArray();
3789-
int i = 0;
3790-
for ( const auto &header : m_reply->rawHeaderList() ) {
3791-
QJSValue pair = m_scriptable->engine()->newArray();
3792-
pair.setProperty( 0, m_scriptable->newByteArray(header) );
3793-
pair.setProperty( 1, m_scriptable->newByteArray(m_reply->rawHeader(header)) );
3794-
headers.setProperty( static_cast<quint32>(i), pair );
3795-
++i;
3796-
}
3797-
3798-
return headers;
3799-
}
3800-
3801-
QJSValue NetworkReply::finished()
3802-
{
3803-
return m_reply->isFinished();
3804-
}
3805-
3806-
NetworkReply::NetworkReply(const QString &url, const QByteArray &postData, Scriptable *scriptable)
3807-
: QObject(scriptable)
3808-
, m_scriptable(scriptable)
3809-
, m_manager(new QNetworkAccessManager(this))
3810-
, m_reply(nullptr)
3811-
{
3812-
if (postData.isEmpty())
3813-
m_reply = m_manager->get(QNetworkRequest(url));
3814-
else
3815-
m_reply = m_manager->post(QNetworkRequest(url), postData);
3816-
3817-
connect(m_reply, &QNetworkReply::readyRead, this, [this](){
3818-
const qint64 available = m_reply->bytesAvailable();
3819-
m_rawData.append( m_reply->read(available) );
3820-
});
3821-
const qint64 available = m_reply->bytesAvailable();
3822-
m_rawData.append( m_reply->read(available) );
3823-
}
3824-
3825-
QJSValue NetworkReply::toScriptValue()
3826-
{
3827-
if ( m_self.isUndefined() )
3828-
m_self = m_scriptable->engine()->newQObject(this);
3829-
return m_self;
3830-
}
3831-
38323723
ScriptablePlugins::ScriptablePlugins(Scriptable *scriptable, ItemFactory *factory)
38333724
: QObject(scriptable)
38343725
, m_scriptable(scriptable)

src/scriptable/scriptable.h

Lines changed: 4 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,10 @@ class Action;
1919
class ScriptableByteArray;
2020
class ClipboardBrowser;
2121
class ItemFactory;
22-
class NetworkReply;
2322
class ScriptableProxy;
2423

2524
class QFile;
2625
class QMimeData;
27-
class QNetworkReply;
28-
class QNetworkAccessManager;
2926
class QJSEngine;
3027

3128
enum class ClipboardOwnership;
@@ -162,6 +159,8 @@ public slots:
162159
QJSValue Dir() const;
163160
QJSValue ItemSelection() const;
164161
QJSValue Settings() const;
162+
QJSValue NetworkRequest() const;
163+
QJSValue NetworkReply() const;
165164

166165
QJSValue version();
167166
QJSValue help();
@@ -470,9 +469,6 @@ public slots:
470469

471470
void interruptibleSleep(int msec);
472471

473-
NetworkReply *networkGetHelper();
474-
NetworkReply *networkPostHelper();
475-
476472
QJSValue newQObject(QObject *obj, const QJSValue &prototype) const;
477473

478474
ScriptableProxy *m_proxy;
@@ -517,44 +513,8 @@ public slots:
517513
QJSValue m_dirPrototype;
518514
QJSValue m_itemSelectionPrototype;
519515
QJSValue m_settingsPrototype;
520-
};
521-
522-
class NetworkReply final : public QObject {
523-
Q_OBJECT
524-
Q_PROPERTY(QJSValue data READ data CONSTANT)
525-
Q_PROPERTY(QJSValue error READ error CONSTANT)
526-
Q_PROPERTY(QJSValue status READ status CONSTANT)
527-
Q_PROPERTY(QJSValue redirect READ redirect CONSTANT)
528-
Q_PROPERTY(QJSValue headers READ headers CONSTANT)
529-
Q_PROPERTY(QJSValue finished READ finished CONSTANT)
530-
531-
public:
532-
static NetworkReply *get(const QString &url, Scriptable *scriptable);
533-
static NetworkReply *post(const QString &url, const QByteArray &postData, Scriptable *scriptable);
534-
535-
~NetworkReply();
536-
537-
QJSValue data();
538-
539-
QJSValue error();
540-
541-
QJSValue status();
542-
QJSValue redirect();
543-
QJSValue headers();
544-
545-
QJSValue finished();
546-
547-
QJSValue toScriptValue();
548-
549-
private:
550-
explicit NetworkReply(const QString &url, const QByteArray &postData, Scriptable *scriptable);
551-
552-
Scriptable *m_scriptable;
553-
QNetworkAccessManager *m_manager;
554-
QNetworkReply *m_reply;
555-
QJSValue m_data;
556-
QJSValue m_self;
557-
QByteArray m_rawData;
516+
QJSValue m_networkRequestPrototype;
517+
QJSValue m_networkReplyPrototype;
558518
};
559519

560520
class ScriptablePlugins final : public QObject {

0 commit comments

Comments
 (0)