Skip to content

Commit

Permalink
[WFS provider] Re-inject custom query parameters in DCP HTTP URLs (fixes
Browse files Browse the repository at this point in the history
 #31026)

Since f1b5987, we honour DCP HTTP endpoints
exposed in GetCapabilities.
But some (all?) servers fail to add in those endpoints the custom query parameters
the user might have injected in the GetCapabilities URL.
So re-add them manually.
  • Loading branch information
rouault committed Sep 14, 2019
1 parent 58a2460 commit 97fab83
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 21 deletions.
59 changes: 44 additions & 15 deletions src/providers/wfs/qgswfsdatasourceuri.cpp
Expand Up @@ -19,6 +19,8 @@
#include "qgswfsdatasourceuri.h"
#include "qgsmessagelog.h"

#include <QUrlQuery>

QgsWFSDataSourceURI::QgsWFSDataSourceURI( const QString &uri )
: mURI( uri )
{
Expand All @@ -45,9 +47,8 @@ QgsWFSDataSourceURI::QgsWFSDataSourceURI( const QString &uri )

QUrl url( uri );
// Transform all param keys to lowercase
QList<queryItem> items( url.queryItems() );
const auto constItems = items;
for ( const queryItem &item : constItems )
const auto items( url.queryItems() );
for ( const queryItem &item : items )
{
url.removeQueryItem( item.first );
url.addQueryItem( item.first.toLower(), item.second );
Expand Down Expand Up @@ -106,9 +107,8 @@ QgsWFSDataSourceURI::QgsWFSDataSourceURI( const QString &uri )
do
{
somethingChanged = false;
QList<queryItem> items( url.queryItems() );
const auto constItems = items;
for ( const queryItem &item : constItems )
const auto items( url.queryItems() );
for ( const queryItem &item : items )
{
const QString lowerName( item.first.toLower() );
if ( lowerName == QgsWFSConstants::URI_PARAM_OUTPUTFORMAT )
Expand All @@ -120,7 +120,10 @@ QgsWFSDataSourceURI::QgsWFSDataSourceURI( const QString &uri )
break;
}
else if ( lowerName == QLatin1String( "service" ) ||
lowerName == QLatin1String( "request" ) )
lowerName == QLatin1String( "request" ) ||
lowerName == QLatin1String( "typename" ) ||
lowerName == QLatin1String( "typenames" ) ||
lowerName == QLatin1String( "version" ) )
{
url.removeQueryItem( item.first );
somethingChanged = true;
Expand Down Expand Up @@ -178,23 +181,49 @@ QUrl QgsWFSDataSourceURI::baseURL( bool bIncludeServiceWFS ) const

QUrl QgsWFSDataSourceURI::requestUrl( const QString &request, const Method &method ) const
{
QString endpoint;
QUrl url;
QUrlQuery urlQuery;
switch ( method )
{
case Post:
endpoint = mPostEndpoints.contains( request ) ?
mPostEndpoints[ request ] : mURI.param( QgsWFSConstants::URI_PARAM_URL );
url = QUrl( mPostEndpoints.contains( request ) ?
mPostEndpoints[ request ] : mURI.param( QgsWFSConstants::URI_PARAM_URL ) );
urlQuery = QUrlQuery( url );
break;
default:
case Get:
endpoint = mGetEndpoints.contains( request ) ?
mGetEndpoints[ request ] : mURI.param( QgsWFSConstants::URI_PARAM_URL );
{
const auto defaultUrl( QUrl( mURI.param( QgsWFSConstants::URI_PARAM_URL ) ) );
if ( mGetEndpoints.contains( request ) )
{
// If the input URL has query parameters, and those are not found in the
// DCP endpoint, then re-inject them.
// I'm not completely sure this is the job of the client to do that.
// One could argue that the server should expose them in the DCP endpoint.
url = QUrl( mGetEndpoints[ request ] );
urlQuery = QUrlQuery( url );
const QUrlQuery defaultUrlQuery( defaultUrl );
const auto itemsDefaultUrl( defaultUrlQuery.queryItems() );
for ( const auto &item : itemsDefaultUrl )
{
if ( !urlQuery.hasQueryItem( item.first ) )
{
urlQuery.addQueryItem( item.first, item.second );
}
}
}
else
{
url = defaultUrl;
urlQuery = QUrlQuery( url );
}
break;
}
}
QUrl url( endpoint );
url.addQueryItem( QStringLiteral( "SERVICE" ), QStringLiteral( "WFS" ) );
urlQuery.addQueryItem( QStringLiteral( "SERVICE" ), QStringLiteral( "WFS" ) );
if ( method == Method::Get && ! request.isEmpty() )
url.addQueryItem( QStringLiteral( "REQUEST" ), request );
urlQuery.addQueryItem( QStringLiteral( "REQUEST" ), request );
url.setQuery( urlQuery );
return url;
}

Expand Down
12 changes: 6 additions & 6 deletions tests/src/python/test_provider_wfs.py
Expand Up @@ -3037,7 +3037,7 @@ def testWFS10DCP(self):
endpoint = self.__class__.basetestpath + '/fake_qgis_http_endpoint_WFS_DCP_1.0'
endpoint_alternate = self.__class__.basetestpath + '/fake_qgis_http_endpoint_WFS_DCP_1.0_alternate'

with open(sanitize(endpoint, '?SERVICE=WFS?REQUEST=GetCapabilities?VERSION=1.0.0'), 'wb') as f:
with open(sanitize(endpoint, '?FOO=BAR&SERVICE=WFS&REQUEST=GetCapabilities&VERSION=1.0.0'), 'wb') as f:
f.write("""
<WFS_Capabilities version="1.0.0" xmlns="http://www.opengis.net/wfs" xmlns:ogc="http://www.opengis.net/ogc">
<FeatureTypeList>
Expand Down Expand Up @@ -3069,7 +3069,7 @@ def testWFS10DCP(self):
</Operation>
</OperationsMetadata></WFS_Capabilities>""".format(endpoint_alternate).encode('UTF-8'))

with open(sanitize(endpoint_alternate, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=1.0.0&TYPENAME=my:typename'), 'wb') as f:
with open(sanitize(endpoint_alternate, '?FOO=BAR&SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=1.0.0&TYPENAME=my:typename'), 'wb') as f:
f.write("""
<xsd:schema xmlns:my="http://my" xmlns:gml="http://www.opengis.net/gml" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://my">
<xsd:import namespace="http://www.opengis.net/gml"/>
Expand All @@ -3092,7 +3092,7 @@ def testWFS10DCP(self):
</xsd:schema>
""".encode('UTF-8'))

vl = QgsVectorLayer("url='http://" + endpoint + "' typename='my:typename' version='1.0.0'", 'test', 'WFS')
vl = QgsVectorLayer("url='http://" + endpoint + "?FOO=BAR&SERVICE=WFS&REQUEST=GetCapabilities&VERSION=1.1.0" + "' typename='my:typename' version='1.0.0'", 'test', 'WFS')
self.assertTrue(vl.isValid())
self.assertEqual(vl.wkbType(), QgsWkbTypes.Point)
self.assertEqual(len(vl.fields()), 5)
Expand All @@ -3101,7 +3101,7 @@ def testWFS10DCP(self):
vl_extent = QgsGeometry.fromRect(vl.extent())
assert QgsGeometry.compare(vl_extent.asPolygon()[0], reference.asPolygon()[0], 0.00001), 'Expected {}, got {}'.format(reference.asWkt(), vl_extent.asWkt())

with open(sanitize(endpoint_alternate, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=1.0.0&TYPENAME=my:typename&SRSNAME=EPSG:32631'), 'wb') as f:
with open(sanitize(endpoint_alternate, '?FOO=BAR&SERVICE=WFS&REQUEST=GetFeature&VERSION=1.0.0&TYPENAME=my:typename&SRSNAME=EPSG:32631'), 'wb') as f:
f.write("""
<wfs:FeatureCollection
xmlns:wfs="http://www.opengis.net/wfs"
Expand Down Expand Up @@ -3156,7 +3156,7 @@ def testWFS10DCP(self):
self.assertFalse(vl.dataProvider().deleteFeatures([0]))

# Test with restrictToRequestBBOX=1
with open(sanitize(endpoint_alternate, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=1.0.0&TYPENAME=my:typename&SRSNAME=EPSG:32631&BBOX=400000,5400000,450000,5500000'), 'wb') as f:
with open(sanitize(endpoint_alternate, '?FOO=BAR&SERVICE=WFS&REQUEST=GetFeature&VERSION=1.0.0&TYPENAME=my:typename&SRSNAME=EPSG:32631&BBOX=400000,5400000,450000,5500000'), 'wb') as f:
f.write("""
<wfs:FeatureCollection
xmlns:wfs="http://www.opengis.net/wfs"
Expand All @@ -3173,7 +3173,7 @@ def testWFS10DCP(self):
</gml:featureMember>
</wfs:FeatureCollection>""".encode('UTF-8'))

vl = QgsVectorLayer("url='http://" + endpoint + "' typename='my:typename' version='1.0.0' restrictToRequestBBOX=1", 'test', 'WFS')
vl = QgsVectorLayer("url='http://" + endpoint + "?FOO=BAR" + "' typename='my:typename' version='1.0.0' restrictToRequestBBOX=1", 'test', 'WFS')

extent = QgsRectangle(400000.0, 5400000.0, 450000.0, 5500000.0)
request = QgsFeatureRequest().setFilterRect(extent)
Expand Down

0 comments on commit 97fab83

Please sign in to comment.