[geonode] Don't block data source manager while connecting to a server

Also add missing docstrings
This commit is contained in:
Nyall Dawson 2017-09-08 11:54:39 +10:00
parent 1a19283634
commit e1562df16b
10 changed files with 517 additions and 227 deletions

View File

@ -12,15 +12,43 @@ struct QgsGeoNodeStyle
%TypeHeaderCode %TypeHeaderCode
#include <qgsgeonoderequest.h> #include <qgsgeonoderequest.h>
%End %End
QString id; QString id;
%Docstring
Unique style ID
%End
QString name; QString name;
%Docstring
Style name
%End
QString title; QString title;
%Docstring
Style title
%End
QDomDocument body; QDomDocument body;
%Docstring
DOM documenting containing style
%End
QString styleUrl; QString styleUrl;
%Docstring
Associated URL
%End
}; };
class QgsGeoNodeRequest : QObject class QgsGeoNodeRequest : QObject
{ {
%Docstring
Request handler for GeoNode servers.
QgsGeoNodeRequest handles requesting and parsing service details from a GeoNode
server instance, for instance requesting all available layers or layer styles.
.. versionadded:: 3.0
%End
%TypeHeaderCode %TypeHeaderCode
#include "qgsgeonoderequest.h" #include "qgsgeonoderequest.h"
@ -30,110 +58,192 @@ class QgsGeoNodeRequest : QObject
struct ServiceLayerDetail struct ServiceLayerDetail
{ {
QUuid uuid; QUuid uuid;
%Docstring
Unique identifier (generate on the client side, not at the GeoNode server)
%End
QString name; QString name;
%Docstring
Layer name
%End
QString typeName; QString typeName;
%Docstring
Layer type name
%End
QString title; QString title;
%Docstring
Layer title
%End
QString wmsURL; QString wmsURL;
%Docstring
WMS URL for layer
%End
QString wfsURL; QString wfsURL;
%Docstring
WFS URL for layer
%End
QString xyzURL; QString xyzURL;
%Docstring
XYZ tileserver URL for layer
%End
}; };
explicit QgsGeoNodeRequest( bool forceRefresh, QObject *parent = 0 ); QgsGeoNodeRequest( const QString &baseUrl, bool forceRefresh, QObject *parent = 0 );
%Docstring %Docstring
Constructor for QgsGeoNodeRequest. Constructor for QgsGeoNodeRequest.
If ``forceRefresh`` is false, then cached copies of the request may be reused. If ``forceRefresh`` is false, then cached copies of the request may be reused.
%End %End
QgsGeoNodeRequest( const QString &baseUrl, bool forceRefresh, QObject *parent = 0 );
virtual ~QgsGeoNodeRequest(); virtual ~QgsGeoNodeRequest();
bool request( const QString &endPoint ); void request( const QString &endPoint );
%Docstring %Docstring
Triggers a new request to the GeoNode server, with the requested ``endPoint``.
Any existing request will be aborted.
Calling this method does not block while waiting for a result.
\warning When using the non-blocking methods in this class, sending
overlapping requests results in undefined behavior. Use separate instances
of QgsGeoNodeRequest instead to avoid this.
.. seealso:: requestBlocking()
%End
bool requestBlocking( const QString &endPoint );
%Docstring
Triggers a new request to the GeoNode server, with the requested ``endPoint``.
Any existing request will be aborted.
Calling this method will block while waiting for a result. It should not be
used from any code which potentially blocks operation in the main GUI thread.
.. seealso:: request()
:rtype: bool :rtype: bool
%End %End
QList<QgsGeoNodeRequest::ServiceLayerDetail> getLayers(); void fetchLayers();
%Docstring %Docstring
Triggers a new request to fetch the list of available layers from the
server. When complete, the layersFetched() signal will be emitted
with the result.
This method is non-blocking and returns immediately.
\warning When using the non-blocking methods in this class, sending
overlapping requests results in undefined behavior. Use separate instances
of QgsGeoNodeRequest instead to avoid this.
.. seealso:: layersFetched()
.. seealso:: fetchLayersBlocking()
%End
QList<QgsGeoNodeRequest::ServiceLayerDetail> fetchLayersBlocking();
%Docstring
Requests the list of available layers from the server.
This method is blocking and will wait for results from the server before returning.
Accordingly it should not be used from any code which potentially blocks operation in the main GUI thread.
.. seealso:: fetchLayers()
:rtype: list of QgsGeoNodeRequest.ServiceLayerDetail :rtype: list of QgsGeoNodeRequest.ServiceLayerDetail
%End %End
QList<QgsGeoNodeStyle> getStyles( const QString &layerName ); QList<QgsGeoNodeStyle> fetchStylesBlocking( const QString &layerName );
%Docstring %Docstring
Requests the list of available styles for the layer
with matching ``layerName`` from the server.
This method is blocking and will wait for results from the server before returning.
Accordingly it should not be used from any code which potentially blocks operation in the main GUI thread.
:rtype: list of QgsGeoNodeStyle :rtype: list of QgsGeoNodeStyle
%End %End
QgsGeoNodeStyle getDefaultStyle( const QString &layerName ); QgsGeoNodeStyle fetchDefaultStyleBlocking( const QString &layerName );
%Docstring %Docstring
Requests the default style for the layer with matching ``layerName`` from the server.
This method is blocking and will wait for results from the server before returning.
Accordingly it should not be used from any code which potentially blocks operation in the main GUI thread.
:rtype: QgsGeoNodeStyle :rtype: QgsGeoNodeStyle
%End %End
QgsGeoNodeStyle getStyle( const QString &styleID ); QgsGeoNodeStyle fetchStyleBlocking( const QString &styleId );
%Docstring %Docstring
Requests the details for the style with matching ``styleId`` from the server.
This method is blocking and will wait for results from the server before returning.
Accordingly it should not be used from any code which potentially blocks operation in the main GUI thread.
:rtype: QgsGeoNodeStyle :rtype: QgsGeoNodeStyle
%End %End
QStringList serviceUrls( const QString &serviceType ); QStringList fetchServiceUrlsBlocking( const QString &serviceType );
%Docstring %Docstring
Obtain list of unique URLs in the geonode Requests the list of unique URLs for available services with matching ``serviceType`` from the server.
This method is blocking and will wait for results from the server before returning.
Accordingly it should not be used from any code which potentially blocks operation in the main GUI thread.
:rtype: list of str :rtype: list of str
%End %End
QgsStringMap serviceUrlData( const QString &serviceType ); QgsStringMap fetchServiceUrlDataBlocking( const QString &serviceType );
%Docstring %Docstring
Obtain map of layer name and url for a service type Obtains a map of layer name to URL for available services with matching ``serviceType`` from the server.
This method is blocking and will wait for results from the server before returning.
Accordingly it should not be used from any code which potentially blocks operation in the main GUI thread.
:rtype: QgsStringMap :rtype: QgsStringMap
%End %End
QString lastError() const; QString lastError() const;
%Docstring %Docstring
Returns the most recent error string for any encountered errors, or an empty string if
no errors have been encountered.
:rtype: str :rtype: str
%End %End
QByteArray response() const; QByteArray lastResponse() const;
%Docstring %Docstring
Returns the most recent response obtained from the server.
:rtype: QByteArray :rtype: QByteArray
%End %End
QNetworkReply *reply() const; QString protocol() const;
%Docstring %Docstring
:rtype: QNetworkReply Returns the network protocol (e.g. 'http') used for connecting with the server.
.. seealso:: setProtocol()
:rtype: str
%End %End
void setProtocol( const QString &protocol );
%Docstring
Sets the network ``protocol`` (e.g. 'http') used for connecting with the server.
.. seealso:: protocol()
%End
public slots:
void abort(); void abort();
%Docstring %Docstring
Abort network request immediately Aborts any active network request immediately.
%End %End
QString getProtocol() const;
%Docstring
:rtype: str
%End
void setProtocol( const QString &protocol );
signals: signals:
void statusChanged( const QString &statusQString ); void statusChanged( const QString &statusQString );
%Docstring %Docstring
emit a signal to be caught by qgisapp and display a statusQString on status bar Emitted when the status of an ongoing request is changed.
%End %End
void requestFinished(); void requestFinished();
%Docstring %Docstring
emit a signal once the request is finished Emitted when the existing request has been completed.
%End %End
protected slots: void layersFetched( const QList<QgsGeoNodeRequest::ServiceLayerDetail> &layers );
void replyFinished(); %Docstring
void replyProgress( qint64, qint64 ); Emitted when the result of a fetchLayers call has been received and processed.
%End
protected:
}; };

View File

@ -38,9 +38,9 @@ QVector<QgsDataItem *> QgsGeoNodeConnectionItem::createChildren()
QString url = mConnection->uri().param( QStringLiteral( "url" ) ); QString url = mConnection->uri().param( QStringLiteral( "url" ) );
QgsGeoNodeRequest geonodeRequest( url, true ); QgsGeoNodeRequest geonodeRequest( url, true );
QStringList wmsUrl = geonodeRequest.serviceUrls( QStringLiteral( "WMS" ) ); QStringList wmsUrl = geonodeRequest.fetchServiceUrlsBlocking( QStringLiteral( "WMS" ) );
QStringList wfsUrl = geonodeRequest.serviceUrls( QStringLiteral( "WFS" ) ); QStringList wfsUrl = geonodeRequest.fetchServiceUrlsBlocking( QStringLiteral( "WFS" ) );
QStringList xyzUrl = geonodeRequest.serviceUrls( QStringLiteral( "XYZ" ) ); QStringList xyzUrl = geonodeRequest.fetchServiceUrlsBlocking( QStringLiteral( "XYZ" ) );
if ( !wmsUrl.isEmpty() ) if ( !wmsUrl.isEmpty() )
{ {

View File

@ -249,7 +249,7 @@ void QgsGeoNodeNewConnection::testConnection()
QString url = txtUrl->text(); QString url = txtUrl->text();
QgsGeoNodeRequest geonodeRequest( url, true ); QgsGeoNodeRequest geonodeRequest( url, true );
QList<QgsGeoNodeRequest::ServiceLayerDetail> layers = geonodeRequest.getLayers(); QList<QgsGeoNodeRequest::ServiceLayerDetail> layers = geonodeRequest.fetchLayersBlocking();
QApplication::restoreOverrideCursor(); QApplication::restoreOverrideCursor();
if ( !layers.empty() ) if ( !layers.empty() )

View File

@ -72,6 +72,11 @@ QgsGeoNodeSourceSelect::QgsGeoNodeSourceSelect( QWidget *parent, Qt::WindowFlags
treeView->setModel( mModelProxy ); treeView->setModel( mModelProxy );
} }
QgsGeoNodeSourceSelect::~QgsGeoNodeSourceSelect()
{
emit abortRequests();
}
void QgsGeoNodeSourceSelect::addConnectionsEntryList() void QgsGeoNodeSourceSelect::addConnectionsEntryList()
{ {
QgsGeoNodeNewConnection nc( this ); QgsGeoNodeNewConnection nc( this );
@ -147,141 +152,146 @@ void QgsGeoNodeSourceSelect::showHelp()
void QgsGeoNodeSourceSelect::connectToGeonodeConnection() void QgsGeoNodeSourceSelect::connectToGeonodeConnection()
{ {
QApplication::setOverrideCursor( Qt::BusyCursor );
QgsGeoNodeConnection connection( cmbConnections->currentText() ); QgsGeoNodeConnection connection( cmbConnections->currentText() );
QString url = connection.uri().param( QStringLiteral( "url" ) ); QString url = connection.uri().param( QStringLiteral( "url" ) );
QgsGeoNodeRequest geonodeRequest( url, true ); QgsGeoNodeRequest *geonodeRequest = new QgsGeoNodeRequest( url, true );
connect( this, &QgsGeoNodeSourceSelect::abortRequests, geonodeRequest, &QgsGeoNodeRequest::abort );
QApplication::setOverrideCursor( Qt::WaitCursor ); connect( geonodeRequest, &QgsGeoNodeRequest::requestFinished, geonodeRequest, [geonodeRequest]
const QList<QgsGeoNodeRequest::ServiceLayerDetail> layers = geonodeRequest.getLayers();
QApplication::restoreOverrideCursor();
if ( !layers.empty() )
{ {
QgsDebugMsg( QStringLiteral( "Success, non empty layers %1" ).arg( layers.count( ) ) ); QApplication::restoreOverrideCursor();
} geonodeRequest->deleteLater();
else } );
connect( geonodeRequest, &QgsGeoNodeRequest::layersFetched, this, [ = ]( const QList< QgsGeoNodeRequest::ServiceLayerDetail > layers )
{ {
QgsMessageLog::logMessage( QStringLiteral( "Failed, empty layers" ), tr( "GeoNode" ) ); if ( !layers.empty() )
}
if ( mModel )
{
mModel->removeRows( 0, mModel->rowCount() );
}
if ( !layers.isEmpty() )
{
for ( const QgsGeoNodeRequest::ServiceLayerDetail &layer : layers )
{ {
QUuid uuid = layer.uuid; QgsDebugMsg( QStringLiteral( "Success, non empty layers %1" ).arg( layers.count( ) ) );
}
else
{
QgsMessageLog::logMessage( QStringLiteral( "Failed, empty layers" ), tr( "GeoNode" ) );
}
QString wmsURL = layer.wmsURL; if ( mModel )
QString wfsURL = layer.wfsURL; {
QString xyzURL = layer.xyzURL; mModel->removeRows( 0, mModel->rowCount() );
}
if ( !wmsURL.isEmpty() ) if ( !layers.isEmpty() )
{
for ( const QgsGeoNodeRequest::ServiceLayerDetail &layer : layers )
{ {
QStandardItem *titleItem = new QStandardItem( layer.title ); QUuid uuid = layer.uuid;
QStandardItem *nameItem;
if ( !layer.name.isEmpty() ) QString wmsURL = layer.wmsURL;
QString wfsURL = layer.wfsURL;
QString xyzURL = layer.xyzURL;
if ( !wmsURL.isEmpty() )
{ {
nameItem = new QStandardItem( layer.name ); QStandardItem *titleItem = new QStandardItem( layer.title );
QStandardItem *nameItem;
if ( !layer.name.isEmpty() )
{
nameItem = new QStandardItem( layer.name );
}
else
{
nameItem = new QStandardItem( layer.title );
}
QStandardItem *serviceTypeItem = new QStandardItem( tr( "Layer" ) );
QStandardItem *webServiceTypeItem = new QStandardItem( tr( "WMS" ) );
QString typeName = layer.typeName;
titleItem->setData( uuid, Qt::UserRole + 1 );
titleItem->setData( wmsURL, Qt::UserRole + 2 );
titleItem->setData( typeName, Qt::UserRole + 3 );
typedef QList< QStandardItem * > StandardItemList;
mModel->appendRow( StandardItemList() << titleItem << nameItem << serviceTypeItem << webServiceTypeItem );
} }
else else
{ {
nameItem = new QStandardItem( layer.title ); QgsDebugMsgLevel( QStringLiteral( "Layer %1 does not have WMS url." ).arg( layer.title ), 3 );
} }
QStandardItem *serviceTypeItem = new QStandardItem( tr( "Layer" ) ); if ( !wfsURL.isEmpty() )
QStandardItem *webServiceTypeItem = new QStandardItem( tr( "WMS" ) );
QString typeName = layer.typeName;
titleItem->setData( uuid, Qt::UserRole + 1 );
titleItem->setData( wmsURL, Qt::UserRole + 2 );
titleItem->setData( typeName, Qt::UserRole + 3 );
typedef QList< QStandardItem * > StandardItemList;
mModel->appendRow( StandardItemList() << titleItem << nameItem << serviceTypeItem << webServiceTypeItem );
}
else
{
QgsDebugMsgLevel( QStringLiteral( "Layer %1 does not have WMS url." ).arg( layer.title ), 3 );
}
if ( !wfsURL.isEmpty() )
{
QStandardItem *titleItem = new QStandardItem( layer.title );
QStandardItem *nameItem;
if ( !layer.name.isEmpty() )
{ {
nameItem = new QStandardItem( layer.name ); QStandardItem *titleItem = new QStandardItem( layer.title );
QStandardItem *nameItem;
if ( !layer.name.isEmpty() )
{
nameItem = new QStandardItem( layer.name );
}
else
{
nameItem = new QStandardItem( layer.title );
}
QStandardItem *serviceTypeItem = new QStandardItem( tr( "Layer" ) );
QStandardItem *webServiceTypeItem = new QStandardItem( tr( "WFS" ) );
QString typeName = layer.typeName;
titleItem->setData( uuid, Qt::UserRole + 1 );
titleItem->setData( wfsURL, Qt::UserRole + 2 );
titleItem->setData( typeName, Qt::UserRole + 3 );
typedef QList< QStandardItem * > StandardItemList;
mModel->appendRow( StandardItemList() << titleItem << nameItem << serviceTypeItem << webServiceTypeItem );
} }
else else
{ {
nameItem = new QStandardItem( layer.title ); QgsDebugMsgLevel( QStringLiteral( "Layer %1 does not have WFS url." ).arg( layer.title ), 3 );
} }
QStandardItem *serviceTypeItem = new QStandardItem( tr( "Layer" ) ); if ( !xyzURL.isEmpty() )
QStandardItem *webServiceTypeItem = new QStandardItem( tr( "WFS" ) );
QString typeName = layer.typeName;
titleItem->setData( uuid, Qt::UserRole + 1 );
titleItem->setData( wfsURL, Qt::UserRole + 2 );
titleItem->setData( typeName, Qt::UserRole + 3 );
typedef QList< QStandardItem * > StandardItemList;
mModel->appendRow( StandardItemList() << titleItem << nameItem << serviceTypeItem << webServiceTypeItem );
}
else
{
QgsDebugMsgLevel( QStringLiteral( "Layer %1 does not have WFS url." ).arg( layer.title ), 3 );
}
if ( !xyzURL.isEmpty() )
{
QStandardItem *titleItem = new QStandardItem( layer.title );
QStandardItem *nameItem;
if ( !layer.name.isEmpty() )
{ {
nameItem = new QStandardItem( layer.name ); QStandardItem *titleItem = new QStandardItem( layer.title );
QStandardItem *nameItem;
if ( !layer.name.isEmpty() )
{
nameItem = new QStandardItem( layer.name );
}
else
{
nameItem = new QStandardItem( layer.title );
}
QStandardItem *serviceTypeItem = new QStandardItem( tr( "Layer" ) );
QStandardItem *webServiceTypeItem = new QStandardItem( tr( "XYZ" ) );
QString typeName = layer.typeName;
titleItem->setData( uuid, Qt::UserRole + 1 );
titleItem->setData( xyzURL, Qt::UserRole + 2 );
titleItem->setData( typeName, Qt::UserRole + 3 );
typedef QList< QStandardItem * > StandardItemList;
mModel->appendRow( StandardItemList() << titleItem << nameItem << serviceTypeItem << webServiceTypeItem );
} }
else else
{ {
nameItem = new QStandardItem( layer.title ); QgsDebugMsgLevel( QStringLiteral( "Layer %1 does not have XYZ url." ).arg( layer.title ), 3 );
} }
QStandardItem *serviceTypeItem = new QStandardItem( tr( "Layer" ) );
QStandardItem *webServiceTypeItem = new QStandardItem( tr( "XYZ" ) );
QString typeName = layer.typeName;
titleItem->setData( uuid, Qt::UserRole + 1 );
titleItem->setData( xyzURL, Qt::UserRole + 2 );
titleItem->setData( typeName, Qt::UserRole + 3 );
typedef QList< QStandardItem * > StandardItemList;
mModel->appendRow( StandardItemList() << titleItem << nameItem << serviceTypeItem << webServiceTypeItem );
}
else
{
QgsDebugMsgLevel( QStringLiteral( "Layer %1 does not have XYZ url." ).arg( layer.title ), 3 );
} }
} }
}
else else
{
QMessageBox::critical( this, tr( "Connect to GeoNode" ), tr( "Cannot get any feature services" ) );
}
treeView->resizeColumnToContents( MODEL_IDX_TITLE );
treeView->resizeColumnToContents( MODEL_IDX_NAME );
treeView->resizeColumnToContents( MODEL_IDX_TYPE );
treeView->resizeColumnToContents( MODEL_IDX_WEB_SERVICE );
for ( int i = MODEL_IDX_TITLE; i < MODEL_IDX_WEB_SERVICE; i++ )
{
if ( treeView->columnWidth( i ) > 210 )
{ {
treeView->setColumnWidth( i, 210 ); QMessageBox::critical( this, tr( "Connect to GeoNode" ), tr( "Cannot get any feature services" ) );
} }
}
QApplication::restoreOverrideCursor(); treeView->resizeColumnToContents( MODEL_IDX_TITLE );
treeView->resizeColumnToContents( MODEL_IDX_NAME );
treeView->resizeColumnToContents( MODEL_IDX_TYPE );
treeView->resizeColumnToContents( MODEL_IDX_WEB_SERVICE );
for ( int i = MODEL_IDX_TITLE; i < MODEL_IDX_WEB_SERVICE; i++ )
{
if ( treeView->columnWidth( i ) > 210 )
{
treeView->setColumnWidth( i, 210 );
}
}
} );
QApplication::setOverrideCursor( Qt::BusyCursor );
geonodeRequest->fetchLayers();
} }
void QgsGeoNodeSourceSelect::saveGeonodeConnection() void QgsGeoNodeSourceSelect::saveGeonodeConnection()

View File

@ -42,11 +42,16 @@ class QgsGeoNodeSourceSelect: public QgsAbstractDataSourceWidget, private Ui::Qg
public: public:
QgsGeoNodeSourceSelect( QWidget *parent SIP_TRANSFERTHIS = nullptr, Qt::WindowFlags fl = QgsGuiUtils::ModalDialogFlags, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::None ); QgsGeoNodeSourceSelect( QWidget *parent SIP_TRANSFERTHIS = nullptr, Qt::WindowFlags fl = QgsGuiUtils::ModalDialogFlags, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::None );
~QgsGeoNodeSourceSelect();
public slots: public slots:
void addButtonClicked() override; void addButtonClicked() override;
signals:
void abortRequests();
private: private:
/** Stores the available CRS for a server connections. /** Stores the available CRS for a server connections.

View File

@ -27,13 +27,6 @@
#include <QUrl> #include <QUrl>
#include <QDomDocument> #include <QDomDocument>
QgsGeoNodeRequest::QgsGeoNodeRequest( bool forceRefresh, QObject *parent )
: QObject( parent )
, mForceRefresh( forceRefresh )
{
}
QgsGeoNodeRequest::QgsGeoNodeRequest( const QString &baseUrl, bool forceRefresh, QObject *parent ) QgsGeoNodeRequest::QgsGeoNodeRequest( const QString &baseUrl, bool forceRefresh, QObject *parent )
: QObject( parent ) : QObject( parent )
, mBaseUrl( baseUrl ) , mBaseUrl( baseUrl )
@ -57,27 +50,51 @@ void QgsGeoNodeRequest::abort()
} }
} }
QList<QgsGeoNodeRequest::ServiceLayerDetail> QgsGeoNodeRequest::getLayers() void QgsGeoNodeRequest::fetchLayers()
{ {
QList<QgsGeoNodeRequest::ServiceLayerDetail> layers; request( QStringLiteral( "/api/layers/" ) );
bool success = request( QStringLiteral( "/api/layers/" ) ); QObject *obj = new QObject( this );
if ( !success )
connect( this, &QgsGeoNodeRequest::requestFinished, obj, [obj, this ]
{ {
return layers; QList<QgsGeoNodeRequest::ServiceLayerDetail> layers;
} if ( mError.isEmpty() )
return parseLayers( this->response() ); {
layers = parseLayers( this->lastResponse() );
}
emit layersFetched( layers );
obj->deleteLater();
} );
} }
QgsGeoNodeStyle QgsGeoNodeRequest::getDefaultStyle( const QString &layerName ) QList<QgsGeoNodeRequest::ServiceLayerDetail> QgsGeoNodeRequest::fetchLayersBlocking()
{
QList<QgsGeoNodeRequest::ServiceLayerDetail> layers;
QEventLoop loop;
connect( this, &QgsGeoNodeRequest::requestFinished, &loop, &QEventLoop::quit );
QObject *obj = new QObject( this );
connect( this, &QgsGeoNodeRequest::layersFetched, obj, [&]( const QList<QgsGeoNodeRequest::ServiceLayerDetail> &fetched )
{
layers = fetched;
} );
fetchLayers();
loop.exec( QEventLoop::ExcludeUserInputEvents );
delete obj;
return layers;
}
QgsGeoNodeStyle QgsGeoNodeRequest::fetchDefaultStyleBlocking( const QString &layerName )
{ {
QgsGeoNodeStyle defaultStyle; QgsGeoNodeStyle defaultStyle;
bool success = request( QStringLiteral( "/api/layers?name=" ) + layerName ); bool success = requestBlocking( QStringLiteral( "/api/layers?name=" ) + layerName );
if ( !success ) if ( !success )
{ {
return defaultStyle; return defaultStyle;
} }
const QJsonDocument jsonDocument = QJsonDocument::fromJson( this->response() ); const QJsonDocument jsonDocument = QJsonDocument::fromJson( this->lastResponse() );
const QJsonObject jsonObject = jsonDocument.object(); const QJsonObject jsonObject = jsonDocument.object();
const QList<QVariant> layers = jsonObject.toVariantMap().value( QStringLiteral( "objects" ) ).toList(); const QList<QVariant> layers = jsonObject.toVariantMap().value( QStringLiteral( "objects" ) ).toList();
if ( layers.count() < 1 ) if ( layers.count() < 1 )
@ -92,16 +109,16 @@ QgsGeoNodeStyle QgsGeoNodeRequest::getDefaultStyle( const QString &layerName )
} }
QList<QgsGeoNodeStyle> QgsGeoNodeRequest::getStyles( const QString &layerName ) QList<QgsGeoNodeStyle> QgsGeoNodeRequest::fetchStylesBlocking( const QString &layerName )
{ {
QList<QgsGeoNodeStyle> geoNodeStyles; QList<QgsGeoNodeStyle> geoNodeStyles;
bool success = request( QStringLiteral( "/api/styles?layer__name=" ) + layerName ); bool success = requestBlocking( QStringLiteral( "/api/styles?layer__name=" ) + layerName );
if ( !success ) if ( !success )
{ {
return geoNodeStyles; return geoNodeStyles;
} }
const QJsonDocument jsonDocument = QJsonDocument::fromJson( this->response() ); const QJsonDocument jsonDocument = QJsonDocument::fromJson( this->lastResponse() );
const QJsonObject jsobObject = jsonDocument.object(); const QJsonObject jsobObject = jsonDocument.object();
const QList<QVariant> styles = jsobObject.toVariantMap().value( QStringLiteral( "objects" ) ).toList(); const QList<QVariant> styles = jsobObject.toVariantMap().value( QStringLiteral( "objects" ) ).toList();
@ -120,9 +137,9 @@ QList<QgsGeoNodeStyle> QgsGeoNodeRequest::getStyles( const QString &layerName )
} }
QgsGeoNodeStyle QgsGeoNodeRequest::getStyle( const QString &styleID ) QgsGeoNodeStyle QgsGeoNodeRequest::fetchStyleBlocking( const QString &styleId )
{ {
QString endPoint = QStringLiteral( "/api/styles/" ) + styleID; QString endPoint = QStringLiteral( "/api/styles/" ) + styleId;
return retrieveStyle( endPoint ); return retrieveStyle( endPoint );
} }
@ -134,7 +151,7 @@ void QgsGeoNodeRequest::replyProgress( qint64 bytesReceived, qint64 bytesTotal )
emit statusChanged( msg ); emit statusChanged( msg );
} }
QString QgsGeoNodeRequest::getProtocol() const QString QgsGeoNodeRequest::protocol() const
{ {
return mProtocol; return mProtocol;
} }
@ -159,7 +176,6 @@ void QgsGeoNodeRequest::replyFinished()
emit statusChanged( QStringLiteral( "GeoNode request redirected." ) ); emit statusChanged( QStringLiteral( "GeoNode request redirected." ) );
const QUrl &toUrl = redirect.toUrl(); const QUrl &toUrl = redirect.toUrl();
mGeoNodeReply->request();
if ( toUrl == mGeoNodeReply->url() ) if ( toUrl == mGeoNodeReply->url() )
{ {
mError = tr( "Redirect loop detected: %1" ).arg( toUrl.toString() ); mError = tr( "Redirect loop detected: %1" ).arg( toUrl.toString() );
@ -237,7 +253,6 @@ void QgsGeoNodeRequest::replyFinished()
} }
emit requestFinished(); emit requestFinished();
} }
QList<QgsGeoNodeRequest::ServiceLayerDetail> QgsGeoNodeRequest::parseLayers( const QByteArray &layerResponse ) QList<QgsGeoNodeRequest::ServiceLayerDetail> QgsGeoNodeRequest::parseLayers( const QByteArray &layerResponse )
@ -354,12 +369,12 @@ QgsGeoNodeStyle QgsGeoNodeRequest::retrieveStyle( const QString &styleUrl )
{ {
QgsGeoNodeStyle geoNodeStyle; QgsGeoNodeStyle geoNodeStyle;
bool success = request( styleUrl ); bool success = requestBlocking( styleUrl );
if ( !success ) if ( !success )
{ {
return geoNodeStyle; return geoNodeStyle;
} }
const QJsonDocument jsonDocument = QJsonDocument::fromJson( this->response() ); const QJsonDocument jsonDocument = QJsonDocument::fromJson( this->lastResponse() );
const QJsonObject jsonObject = jsonDocument.object(); const QJsonObject jsonObject = jsonDocument.object();
const QVariantMap jsonMap = jsonObject.toVariantMap(); const QVariantMap jsonMap = jsonObject.toVariantMap();
@ -368,13 +383,13 @@ QgsGeoNodeStyle QgsGeoNodeRequest::retrieveStyle( const QString &styleUrl )
geoNodeStyle.title = jsonMap.value( QStringLiteral( "title" ) ).toString(); geoNodeStyle.title = jsonMap.value( QStringLiteral( "title" ) ).toString();
geoNodeStyle.styleUrl = jsonMap.value( QStringLiteral( "style_url" ) ).toString(); geoNodeStyle.styleUrl = jsonMap.value( QStringLiteral( "style_url" ) ).toString();
success = request( geoNodeStyle.styleUrl ); success = requestBlocking( geoNodeStyle.styleUrl );
if ( !success ) if ( !success )
{ {
return geoNodeStyle; return geoNodeStyle;
} }
success = geoNodeStyle.body.setContent( this->response() ); success = geoNodeStyle.body.setContent( this->lastResponse() );
if ( !success ) if ( !success )
{ {
return geoNodeStyle; return geoNodeStyle;
@ -383,11 +398,11 @@ QgsGeoNodeStyle QgsGeoNodeRequest::retrieveStyle( const QString &styleUrl )
return geoNodeStyle; return geoNodeStyle;
} }
QStringList QgsGeoNodeRequest::serviceUrls( const QString &serviceType ) QStringList QgsGeoNodeRequest::fetchServiceUrlsBlocking( const QString &serviceType )
{ {
QStringList urls; QStringList urls;
const QList<QgsGeoNodeRequest::ServiceLayerDetail> layers = getLayers(); const QList<QgsGeoNodeRequest::ServiceLayerDetail> layers = fetchLayersBlocking();
if ( layers.empty() ) if ( layers.empty() )
{ {
@ -415,7 +430,7 @@ QStringList QgsGeoNodeRequest::serviceUrls( const QString &serviceType )
if ( !url.contains( QLatin1String( "://" ) ) ) if ( !url.contains( QLatin1String( "://" ) ) )
{ {
url.prepend( getProtocol() ); url.prepend( protocol() );
} }
if ( !urls.contains( url ) ) if ( !urls.contains( url ) )
{ {
@ -426,11 +441,11 @@ QStringList QgsGeoNodeRequest::serviceUrls( const QString &serviceType )
return urls; return urls;
} }
QgsStringMap QgsGeoNodeRequest::serviceUrlData( const QString &serviceType ) QgsStringMap QgsGeoNodeRequest::fetchServiceUrlDataBlocking( const QString &serviceType )
{ {
QgsStringMap urls; QgsStringMap urls;
const QList<QgsGeoNodeRequest::ServiceLayerDetail> layers = getLayers(); const QList<QgsGeoNodeRequest::ServiceLayerDetail> layers = fetchLayersBlocking();
if ( layers.empty() ) if ( layers.empty() )
{ {
@ -460,7 +475,7 @@ QgsStringMap QgsGeoNodeRequest::serviceUrlData( const QString &serviceType )
QString layerName = layer.name; QString layerName = layer.name;
if ( !url.contains( QLatin1String( "://" ) ) ) if ( !url.contains( QLatin1String( "://" ) ) )
{ {
url.prepend( getProtocol() ); url.prepend( protocol() );
} }
if ( !urls.contains( url ) ) if ( !urls.contains( url ) )
{ {
@ -471,7 +486,7 @@ QgsStringMap QgsGeoNodeRequest::serviceUrlData( const QString &serviceType )
return urls; return urls;
} }
bool QgsGeoNodeRequest::request( const QString &endPoint ) void QgsGeoNodeRequest::request( const QString &endPoint )
{ {
abort(); abort();
mIsAborted = false; mIsAborted = false;
@ -480,25 +495,35 @@ bool QgsGeoNodeRequest::request( const QString &endPoint )
QgsDebugMsg( "Requesting to " + url ); QgsDebugMsg( "Requesting to " + url );
setProtocol( url.split( QStringLiteral( "://" ) ).at( 0 ) ); setProtocol( url.split( QStringLiteral( "://" ) ).at( 0 ) );
QUrl layerUrl( url ); QUrl layerUrl( url );
layerUrl.setScheme( getProtocol() ); layerUrl.setScheme( protocol() );
mError.clear(); mError.clear();
mGeoNodeReply = requestUrl( url );
connect( mGeoNodeReply, &QNetworkReply::finished, this, &QgsGeoNodeRequest::replyFinished, Qt::DirectConnection );
connect( mGeoNodeReply, &QNetworkReply::downloadProgress, this, &QgsGeoNodeRequest::replyProgress, Qt::DirectConnection );
}
bool QgsGeoNodeRequest::requestBlocking( const QString &endPoint )
{
request( endPoint );
QEventLoop loop;
connect( this, &QgsGeoNodeRequest::requestFinished, &loop, &QEventLoop::quit );
loop.exec( QEventLoop::ExcludeUserInputEvents );
return mError.isEmpty();
}
QNetworkReply *QgsGeoNodeRequest::requestUrl( const QString &url )
{
QNetworkRequest request( url ); QNetworkRequest request( url );
// Add authentication check here // Add authentication check here
request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache ); request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true ); request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
mGeoNodeReply = QgsNetworkAccessManager::instance()->get( request ); return QgsNetworkAccessManager::instance()->get( request );
connect( mGeoNodeReply, &QNetworkReply::finished, this, &QgsGeoNodeRequest::replyFinished, Qt::DirectConnection );
connect( mGeoNodeReply, &QNetworkReply::downloadProgress, this, &QgsGeoNodeRequest::replyProgress, Qt::DirectConnection );
QEventLoop loop;
connect( this, &QgsGeoNodeRequest::requestFinished, &loop, &QEventLoop::quit );
loop.exec( QEventLoop::ExcludeUserInputEvents );
return mError.isEmpty();
} }

View File

@ -22,6 +22,12 @@
#include <QObject> #include <QObject>
#include <QUuid> #include <QUuid>
/**
* \ingroup core
* \class QgsGeoNodeStyle
* \brief Encapsulates information about a GeoNode layer style.
* \since QGIS 3.0
*/
struct CORE_EXPORT QgsGeoNodeStyle struct CORE_EXPORT QgsGeoNodeStyle
{ {
#ifdef SIP_RUN #ifdef SIP_RUN
@ -29,26 +35,56 @@ struct CORE_EXPORT QgsGeoNodeStyle
#include <qgsgeonoderequest.h> #include <qgsgeonoderequest.h>
% End % End
#endif #endif
//! Unique style ID
QString id; QString id;
//! Style name
QString name; QString name;
//! Style title
QString title; QString title;
//! DOM documenting containing style
QDomDocument body; QDomDocument body;
//! Associated URL
QString styleUrl; QString styleUrl;
}; };
/**
* \ingroup core
* \class QgsGeoNodeRequest
* \brief Request handler for GeoNode servers.
*
* QgsGeoNodeRequest handles requesting and parsing service details from a GeoNode
* server instance, for instance requesting all available layers or layer styles.
*
* \since QGIS 3.0
*/
class CORE_EXPORT QgsGeoNodeRequest : public QObject class CORE_EXPORT QgsGeoNodeRequest : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
/**
* Service layer details for an individual layer from a GeoNode connection.
*/
struct ServiceLayerDetail struct ServiceLayerDetail
{ {
//! Unique identifier (generate on the client side, not at the GeoNode server)
QUuid uuid; QUuid uuid;
//! Layer name
QString name; QString name;
//! Layer type name
QString typeName; QString typeName;
//! Layer title
QString title; QString title;
//! WMS URL for layer
QString wmsURL; QString wmsURL;
//! WFS URL for layer
QString wfsURL; QString wfsURL;
//! XYZ tileserver URL for layer
QString xyzURL; QString xyzURL;
}; };
@ -57,54 +93,155 @@ class CORE_EXPORT QgsGeoNodeRequest : public QObject
* *
* If \a forceRefresh is false, then cached copies of the request may be reused. * If \a forceRefresh is false, then cached copies of the request may be reused.
*/ */
explicit QgsGeoNodeRequest( bool forceRefresh, QObject *parent = nullptr );
QgsGeoNodeRequest( const QString &baseUrl, bool forceRefresh, QObject *parent = nullptr ); QgsGeoNodeRequest( const QString &baseUrl, bool forceRefresh, QObject *parent = nullptr );
virtual ~QgsGeoNodeRequest(); virtual ~QgsGeoNodeRequest();
bool request( const QString &endPoint ); /**
* Triggers a new request to the GeoNode server, with the requested \a endPoint.
* Any existing request will be aborted.
*
* Calling this method does not block while waiting for a result.
*
* \warning When using the non-blocking methods in this class, sending
* overlapping requests results in undefined behavior. Use separate instances
* of QgsGeoNodeRequest instead to avoid this.
*
* \see requestBlocking()
*/
void request( const QString &endPoint );
QList<QgsGeoNodeRequest::ServiceLayerDetail> getLayers(); /**
* Triggers a new request to the GeoNode server, with the requested \a endPoint.
* Any existing request will be aborted.
*
* Calling this method will block while waiting for a result. It should not be
* used from any code which potentially blocks operation in the main GUI thread.
*
* \see request()
*/
bool requestBlocking( const QString &endPoint );
QList<QgsGeoNodeStyle> getStyles( const QString &layerName ); /**
* Triggers a new request to fetch the list of available layers from the
* server. When complete, the layersFetched() signal will be emitted
* with the result.
*
* This method is non-blocking and returns immediately.
*
* \warning When using the non-blocking methods in this class, sending
* overlapping requests results in undefined behavior. Use separate instances
* of QgsGeoNodeRequest instead to avoid this.
*
* \see layersFetched()
* \see fetchLayersBlocking()
*/
void fetchLayers();
QgsGeoNodeStyle getDefaultStyle( const QString &layerName ); /**
* Requests the list of available layers from the server.
*
* This method is blocking and will wait for results from the server before returning.
* Accordingly it should not be used from any code which potentially blocks operation in the main GUI thread.
*
* \see fetchLayers()
*/
QList<QgsGeoNodeRequest::ServiceLayerDetail> fetchLayersBlocking();
QgsGeoNodeStyle getStyle( const QString &styleID ); /**
* Requests the list of available styles for the layer
* with matching \a layerName from the server.
*
* This method is blocking and will wait for results from the server before returning.
* Accordingly it should not be used from any code which potentially blocks operation in the main GUI thread.
*
*/
QList<QgsGeoNodeStyle> fetchStylesBlocking( const QString &layerName );
//! Obtain list of unique URLs in the geonode /**
QStringList serviceUrls( const QString &serviceType ); * Requests the default style for the layer with matching \a layerName from the server.
*
* This method is blocking and will wait for results from the server before returning.
* Accordingly it should not be used from any code which potentially blocks operation in the main GUI thread.
*/
QgsGeoNodeStyle fetchDefaultStyleBlocking( const QString &layerName );
//! Obtain map of layer name and url for a service type /**
QgsStringMap serviceUrlData( const QString &serviceType ); * Requests the details for the style with matching \a styleId from the server.
*
* This method is blocking and will wait for results from the server before returning.
* Accordingly it should not be used from any code which potentially blocks operation in the main GUI thread.
*/
QgsGeoNodeStyle fetchStyleBlocking( const QString &styleId );
/**
* Requests the list of unique URLs for available services with matching \a serviceType from the server.
*
* This method is blocking and will wait for results from the server before returning.
* Accordingly it should not be used from any code which potentially blocks operation in the main GUI thread.
*/
QStringList fetchServiceUrlsBlocking( const QString &serviceType );
/**
* Obtains a map of layer name to URL for available services with matching \a serviceType from the server.
*
* This method is blocking and will wait for results from the server before returning.
* Accordingly it should not be used from any code which potentially blocks operation in the main GUI thread.
*/
QgsStringMap fetchServiceUrlDataBlocking( const QString &serviceType );
/**
* Returns the most recent error string for any encountered errors, or an empty string if
* no errors have been encountered.
*/
QString lastError() const { return mError; } QString lastError() const { return mError; }
QByteArray response() const { return mHttpGeoNodeResponse; } /**
* Returns the most recent response obtained from the server.
*/
QByteArray lastResponse() const { return mHttpGeoNodeResponse; }
QNetworkReply *reply() const { return mGeoNodeReply; } /**
* Returns the network protocol (e.g. 'http') used for connecting with the server.
* \see setProtocol()
*/
QString protocol() const;
//! Abort network request immediately /**
void abort(); * Sets the network \a protocol (e.g. 'http') used for connecting with the server.
* \see protocol()
QString getProtocol() const; */
void setProtocol( const QString &protocol ); void setProtocol( const QString &protocol );
private: public slots:
QList<QgsGeoNodeRequest::ServiceLayerDetail> parseLayers( const QByteArray &layerResponse );
QgsGeoNodeStyle retrieveStyle( const QString &styleUrl ); /**
* Aborts any active network request immediately.
*/
void abort();
signals: signals:
//! \brief emit a signal to be caught by qgisapp and display a statusQString on status bar
/**
* Emitted when the status of an ongoing request is changed.
*/
void statusChanged( const QString &statusQString ); void statusChanged( const QString &statusQString );
//! \brief emit a signal once the request is finished /**
* Emitted when the existing request has been completed.
*/
void requestFinished(); void requestFinished();
protected slots: /**
* Emitted when the result of a fetchLayers call has been received and processed.
*/
void layersFetched( const QList<QgsGeoNodeRequest::ServiceLayerDetail> &layers );
private slots:
void replyFinished(); void replyFinished();
void replyProgress( qint64, qint64 ); void replyProgress( qint64, qint64 );
protected: private:
//! URL part of URI (httpuri) //! URL part of URI (httpuri)
QString mProtocol; QString mProtocol;
@ -112,8 +249,6 @@ class CORE_EXPORT QgsGeoNodeRequest : public QObject
//! URL part of URI (httpuri) //! URL part of URI (httpuri)
QString mBaseUrl; QString mBaseUrl;
// QgsWmsAuthorization mAuth;
//! The reply to the geonode request //! The reply to the geonode request
QNetworkReply *mGeoNodeReply = nullptr; QNetworkReply *mGeoNodeReply = nullptr;
@ -129,6 +264,11 @@ class CORE_EXPORT QgsGeoNodeRequest : public QObject
bool mIsAborted = false; bool mIsAborted = false;
bool mForceRefresh = false; bool mForceRefresh = false;
QList<QgsGeoNodeRequest::ServiceLayerDetail> parseLayers( const QByteArray &layerResponse );
QgsGeoNodeStyle retrieveStyle( const QString &styleUrl );
QNetworkReply *requestUrl( const QString &url );
}; };
#endif // QGSGEONODEREQUEST_H #endif // QGSGEONODEREQUEST_H

View File

@ -227,7 +227,7 @@ QgsDataItem *QgsWfsDataItemProvider::createDataItem( const QString &path, QgsDat
QString url = connection.uri().param( "url" ); QString url = connection.uri().param( "url" );
QgsGeoNodeRequest geonodeRequest( url, true ); QgsGeoNodeRequest geonodeRequest( url, true );
QgsWFSDataSourceURI sourceUri( geonodeRequest.serviceUrls( QStringLiteral( "WFS" ) )[0] ); QgsWFSDataSourceURI sourceUri( geonodeRequest.fetchServiceUrlsBlocking( QStringLiteral( "WFS" ) )[0] );
QgsDebugMsg( QString( "WFS full uri: '%1'." ).arg( QString( sourceUri.uri() ) ) ); QgsDebugMsg( QString( "WFS full uri: '%1'." ).arg( QString( sourceUri.uri() ) ) );
@ -251,7 +251,7 @@ QVector<QgsDataItem *> QgsWfsDataItemProvider::createDataItems( const QString &p
QString url = connection.uri().param( "url" ); QString url = connection.uri().param( "url" );
QgsGeoNodeRequest geonodeRequest( url, true ); QgsGeoNodeRequest geonodeRequest( url, true );
QStringList encodedUris( geonodeRequest.serviceUrls( QStringLiteral( "WFS" ) ) ); QStringList encodedUris( geonodeRequest.fetchServiceUrlsBlocking( QStringLiteral( "WFS" ) ) );
if ( !encodedUris.isEmpty() ) if ( !encodedUris.isEmpty() )
{ {

View File

@ -566,7 +566,7 @@ QVector<QgsDataItem *> QgsWmsDataItemProvider::createDataItems( const QString &p
QString url = connection.uri().param( "url" ); QString url = connection.uri().param( "url" );
QgsGeoNodeRequest geonodeRequest( url, true ); QgsGeoNodeRequest geonodeRequest( url, true );
QStringList encodedUris( geonodeRequest.serviceUrls( QStringLiteral( "WMS" ) ) ); QStringList encodedUris( geonodeRequest.fetchServiceUrlsBlocking( QStringLiteral( "WMS" ) ) );
if ( !encodedUris.isEmpty() ) if ( !encodedUris.isEmpty() )
{ {
@ -612,7 +612,7 @@ QVector<QgsDataItem *> QgsXyzTileDataItemProvider::createDataItems( const QStrin
QString url = connection.uri().param( "url" ); QString url = connection.uri().param( "url" );
QgsGeoNodeRequest geonodeRequest( url, true ); QgsGeoNodeRequest geonodeRequest( url, true );
QgsStringMap urlData( geonodeRequest.serviceUrlData( QStringLiteral( "XYZ" ) ) ); QgsStringMap urlData( geonodeRequest.fetchServiceUrlDataBlocking( QStringLiteral( "XYZ" ) ) );
if ( !urlData.isEmpty() ) if ( !urlData.isEmpty() )
{ {

View File

@ -129,7 +129,7 @@ void TestQgsGeoNodeConnection::testLayerAPI()
} }
QgsGeoNodeRequest geonodeRequest( mKartozaGeoNodeQGISServerURL, true ); QgsGeoNodeRequest geonodeRequest( mKartozaGeoNodeQGISServerURL, true );
QList<QgsGeoNodeRequest::ServiceLayerDetail> layers = geonodeRequest.getLayers(); QList<QgsGeoNodeRequest::ServiceLayerDetail> layers = geonodeRequest.fetchLayersBlocking();
QString msg = QStringLiteral( "Number of layers: %1" ).arg( layers.count() ); QString msg = QStringLiteral( "Number of layers: %1" ).arg( layers.count() );
QgsDebugMsg( msg ); QgsDebugMsg( msg );
QVERIFY( layers.count() > 0 ); QVERIFY( layers.count() > 0 );
@ -144,17 +144,17 @@ void TestQgsGeoNodeConnection::testStyleAPI()
} }
QgsGeoNodeRequest geonodeRequest( mKartozaGeoNodeQGISServerURL, true ); QgsGeoNodeRequest geonodeRequest( mKartozaGeoNodeQGISServerURL, true );
QgsGeoNodeStyle defaultStyle = geonodeRequest.getDefaultStyle( QStringLiteral( "airports" ) ); QgsGeoNodeStyle defaultStyle = geonodeRequest.fetchDefaultStyleBlocking( QStringLiteral( "airports" ) );
QVERIFY( !defaultStyle.name.isEmpty() ); QVERIFY( !defaultStyle.name.isEmpty() );
QVERIFY( defaultStyle.body.toString().startsWith( QStringLiteral( "<qgis" ) ) ); QVERIFY( defaultStyle.body.toString().startsWith( QStringLiteral( "<qgis" ) ) );
QVERIFY( defaultStyle.body.toString().contains( QStringLiteral( "</qgis>" ) ) ); QVERIFY( defaultStyle.body.toString().contains( QStringLiteral( "</qgis>" ) ) );
QgsGeoNodeStyle geoNodeStyle = geonodeRequest.getStyle( "76" ); QgsGeoNodeStyle geoNodeStyle = geonodeRequest.fetchStyleBlocking( "76" );
QVERIFY( !geoNodeStyle.name.isEmpty() ); QVERIFY( !geoNodeStyle.name.isEmpty() );
QVERIFY( geoNodeStyle.body.toString().startsWith( QStringLiteral( "<qgis" ) ) ); QVERIFY( geoNodeStyle.body.toString().startsWith( QStringLiteral( "<qgis" ) ) );
QVERIFY( geoNodeStyle.body.toString().contains( QStringLiteral( "</qgis>" ) ) ); QVERIFY( geoNodeStyle.body.toString().contains( QStringLiteral( "</qgis>" ) ) );
QList<QgsGeoNodeStyle> geoNodeStyles = geonodeRequest.getStyles( QStringLiteral( "airports" ) ); QList<QgsGeoNodeStyle> geoNodeStyles = geonodeRequest.fetchStylesBlocking( QStringLiteral( "airports" ) );
QgsDebugMsg( geoNodeStyles.count() ); QgsDebugMsg( geoNodeStyles.count() );
QVERIFY( geoNodeStyles.count() == 2 ); QVERIFY( geoNodeStyles.count() == 2 );