Skip to content
Permalink
Browse files

[WFS provider] Handle buggy servers that require plural form TYPENAME…

…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 2330b7fc63e8c72e045cb4676d898cb84fec337b
@@ -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 );
}
@@ -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;
@@ -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();
@@ -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;
@@ -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.
You can’t perform that action at this time.