Skip to content

Commit

Permalink
[WFS provider] Handle buggy servers that require plural form TYPENAME…
Browse files Browse the repository at this point in the history
…S for DescribeFeatureType (fixes #18882, refs #17872)

Some servers like http://geoportal.samregion.ru/wfs12 return an
error when issuing a REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAME=...
query, which is the conformant way
They expect the plural form TYPENAMES to be passed
As they return an exception when being provided with the singular form,
we can automate the retry with TYPENAMES.
  • Loading branch information
rouault committed May 23, 2018
1 parent 7511d1f commit 2330b7f
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 17 deletions.
5 changes: 3 additions & 2 deletions src/providers/wfs/qgswfsdescribefeaturetype.cpp
Expand Up @@ -21,11 +21,12 @@ QgsWFSDescribeFeatureType::QgsWFSDescribeFeatureType( QgsWFSDataSourceURI &uri )
}

bool QgsWFSDescribeFeatureType::requestFeatureType( const QString &WFSVersion,
const QString &typeName )
const QString &typeName, bool bUsePlural )
{
QUrl url( mUri.requestUrl( QStringLiteral( "DescribeFeatureType" ) ) );
url.addQueryItem( QStringLiteral( "VERSION" ), WFSVersion );
url.addQueryItem( QStringLiteral( "TYPENAME" ), typeName );
url.addQueryItem( bUsePlural ?
QStringLiteral( "TYPENAMES" ) : QStringLiteral( "TYPENAME" ), typeName );

return sendGET( url, true, false );
}
Expand Down
2 changes: 1 addition & 1 deletion src/providers/wfs/qgswfsdescribefeaturetype.h
Expand Up @@ -25,7 +25,7 @@ class QgsWFSDescribeFeatureType : public QgsWfsRequest
explicit QgsWFSDescribeFeatureType( QgsWFSDataSourceURI &uri );

//! Issue the request
bool requestFeatureType( const QString &WFSVersion, const QString &typeName );
bool requestFeatureType( const QString &WFSVersion, const QString &typeName, bool bUsePlural );

protected:
QString errorMessageWithReason( const QString &reason ) override;
Expand Down
60 changes: 46 additions & 14 deletions src/providers/wfs/qgswfsprovider.cpp
Expand Up @@ -420,15 +420,31 @@ bool QgsWFSProvider::processSQL( const QString &sqlString, QString &errorMsg, QS
}

QgsWFSDescribeFeatureType describeFeatureType( mShared->mURI );
if ( !describeFeatureType.requestFeatureType( mShared->mWFSVersion,
concatenatedTypenames ) )
bool bUsePlural = false;
QByteArray response;
for ( int i = 0; i < 2; i++ )
{
errorMsg = tr( "DescribeFeatureType failed for url %1: %2" ).
arg( dataSourceUri(), describeFeatureType.errorMessage() );
return false;
}
if ( !describeFeatureType.requestFeatureType( mShared->mWFSVersion,
concatenatedTypenames, bUsePlural ) )
{
errorMsg = tr( "DescribeFeatureType failed for url %1: %2" ).
arg( dataSourceUri(), describeFeatureType.errorMessage() );
return false;
}

const QByteArray &response = describeFeatureType.response();
response = describeFeatureType.response();
// "http://geoportal.samregion.ru/wfs12?SERVICE=WFS&REQUEST=DescribeFeatureType&TYPENAME=EC_1_132&VERSION=2.0.0"
// returns a <ExceptionText><![CDATA[Missing typeNames parameter]]></ExceptionText>
if ( i == 0 && response.indexOf( "<![CDATA[Missing typeNames parameter]]>" ) >= 0 )
{
QgsDebugMsg( "Server does not accept TYPENAME parameter for DescribeFeatureType. Re-trying with TYPENAMES" );
bUsePlural = true;
}
else
{
break;
}
}

QDomDocument describeFeatureDocument;
errorMsg.clear();
Expand Down Expand Up @@ -1165,15 +1181,31 @@ bool QgsWFSProvider::describeFeatureType( QString &geometryAttribute, QgsFields
fields.clear();

QgsWFSDescribeFeatureType describeFeatureType( mShared->mURI );
if ( !describeFeatureType.requestFeatureType( mShared->mWFSVersion,
mShared->mURI.typeName() ) )
bool bUsePlural = false;
QByteArray response;
for ( int i = 0; i < 2; i++ )
{
QgsMessageLog::logMessage( tr( "DescribeFeatureType network request failed for url %1: %2" ).
arg( dataSourceUri(), describeFeatureType.errorMessage() ), tr( "WFS" ) );
return false;
}
if ( !describeFeatureType.requestFeatureType( mShared->mWFSVersion,
mShared->mURI.typeName(), bUsePlural ) )
{
QgsMessageLog::logMessage( tr( "DescribeFeatureType network request failed for url %1: %2" ).
arg( dataSourceUri(), describeFeatureType.errorMessage() ), tr( "WFS" ) );
return false;
}

const QByteArray &response = describeFeatureType.response();
response = describeFeatureType.response();
// "http://geoportal.samregion.ru/wfs12?SERVICE=WFS&REQUEST=DescribeFeatureType&TYPENAME=EC_1_132&VERSION=2.0.0"
// returns a <ExceptionText><![CDATA[Missing typeNames parameter]]></ExceptionText>
if ( i == 0 && response.indexOf( "<![CDATA[Missing typeNames parameter]]>" ) >= 0 )
{
QgsDebugMsg( "Server does not accept TYPENAME parameter for DescribeFeatureType. Re-trying with TYPENAMES" );
bUsePlural = true;
}
else
{
break;
}
}

QDomDocument describeFeatureDocument;
QString errorMsg;
Expand Down
52 changes: 52 additions & 0 deletions tests/src/python/test_provider_wfs.py
Expand Up @@ -3221,6 +3221,58 @@ def testWfs20SamServer(self):
geom_string = re.sub(r'\.\d+', '', geom_string)[:100]
self.assertEqual(geom_string, "LineString (9540051 5997366, 9539934 5997127, 9539822 5996862, 9539504 5996097, 9539529 5996093, 953")

def testWfs20DescribeFeatureTypePluralForm(self):
"""Specs are inconsistent and some 2.0 servers use the TYPENAMES plural form"""

endpoint = self.__class__.basetestpath + '/fake_qgis_http_endpoint_WFS_2.0_describe_typenames'

with open(sanitize(endpoint, '?SERVICE=WFS?REQUEST=GetCapabilities?ACCEPTVERSIONS=2.0.0,1.1.0,1.0.0'), 'wb') as f:
f.write("""
<wfs:WFS_Capabilities version="2.0.0" xmlns="http://www.opengis.net/wfs/2.0" xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:gml="http://schemas.opengis.net/gml/3.2" xmlns:fes="http://www.opengis.net/fes/2.0">
<FeatureTypeList>
<FeatureType>
<Name>my:typename</Name>
<DefaultCRS>urn:ogc:def:crs:EPSG::4326</DefaultCRS>
</FeatureType>
</FeatureTypeList>
</wfs:WFS_Capabilities>""".encode('UTF-8'))

with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAME=my:typename'), 'wb') as f:
f.write("""
<?xml version="1.0"?>
<ExceptionReport
version="2.0.0"
xmlns="http://www.opengis.net/ows/1.1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.opengis.net/ows/1.1
http://schemas.opengis.net/ows/1.1.0/owsAll.xsd">
<Exception exceptionCode="MissingParameterValue" locator="">
<ExceptionText><![CDATA[Missing typeNames parameter]]></ExceptionText>
</Exception>
</ExceptionReport
""".encode('UTF-8'))

with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAMES=my:typename'), 'wb') as f:
f.write("""
<xsd:schema xmlns:my="http://my" xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://my">
<xsd:import namespace="http://www.opengis.net/gml/3.2"/>
<xsd:complexType name="typenameType">
<xsd:complexContent>
<xsd:extension base="gml:AbstractFeatureType">
<xsd:sequence>
<xsd:element maxOccurs="1" minOccurs="0" name="geometryProperty" nillable="true" type="gml:PointPropertyType"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:element name="typename" substitutionGroup="gml:_Feature" type="my:typenameType"/>
</xsd:schema>
""".encode('UTF-8'))

# Create test layer
vl = QgsVectorLayer("url='http://" + endpoint + "' typename='my:typename'", 'test', 'WFS')
self.assertTrue(vl.isValid())


if __name__ == '__main__':
unittest.main()

0 comments on commit 2330b7f

Please sign in to comment.