Skip to content

Commit

Permalink
[bugfix] WFS-T Fixes #15597 #16043
Browse files Browse the repository at this point in the history
This commit fixes a few bugs on WFS-T with
servers that support WFS-T > 1.0.0 when user
configure version != 1.0.0 ("auto" is the default).

It also fixes WFS-T multiple operations on GeoServer
when an insert operation is among them and the feature
store does not return generated feature ids for the
inserted features (i.e. shapefiles).

Tested on GeoServer and QGIS Server

 (cherry-picked from 502a8da)
  • Loading branch information
elpaso committed Jan 16, 2017
1 parent 6d23b04 commit 9324bda
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 10 deletions.
34 changes: 25 additions & 9 deletions src/providers/wfs/qgswfscapabilities.cpp
Expand Up @@ -246,11 +246,31 @@ void QgsWfsCapabilities::capabilitiesReplyFinished()
}

// Parse operations supported for all feature types
bool insertCap, updateCap, deleteCap;
parseSupportedOperations( featureTypeListElem.firstChildElement( QStringLiteral( "Operations" ) ),
insertCap,
updateCap,
deleteCap );
bool insertCap = false;
bool updateCap = false;
bool deleteCap = false;
// WFS < 2
if ( mCaps.version.startsWith( QLatin1String( "1" ) ) )
{
parseSupportedOperations( featureTypeListElem.firstChildElement( QStringLiteral( "Operations" ) ),
insertCap,
updateCap,
deleteCap );
}
else // WFS 2.0.0 tested on GeoServer
{
QDomNodeList operationNodes = doc.elementsByTagName( "Operation" );
for ( int i = 0; i < operationNodes.count(); i++ )
{
QDomElement operationElement = operationNodes.at( i ).toElement( );
if ( operationElement.isElement( ) && "Transaction" == operationElement.attribute( "name" ) )
{
insertCap = true;
updateCap = true;
deleteCap = true;
}
}
}

// get the <FeatureType> elements
QDomNodeList featureTypeList = featureTypeListElem.elementsByTagName( QStringLiteral( "FeatureType" ) );
Expand Down Expand Up @@ -474,10 +494,6 @@ void QgsWfsCapabilities::parseSupportedOperations( const QDomElement& operations
updateCap = false;
deleteCap = false;

// TODO: remove me when WFS-T 1.1 or 2.0 is done
if ( !mCaps.version.startsWith( QLatin1String( "1.0" ) ) )
return;

if ( operationsElem.isNull() )
{
return;
Expand Down
15 changes: 14 additions & 1 deletion src/providers/wfs/qgswfsprovider.cpp
Expand Up @@ -852,6 +852,16 @@ bool QgsWFSProvider::addFeatures( QgsFeatureList &flist )
{
//transaction successful. Add the features to the cache
QStringList idList = insertedFeatureIds( serverResponse );
/* Fix issue with GeoServer and shapefile feature stores when no real
feature id are returned but new0 returned instead of the featureId*/
Q_FOREACH ( const QString &v, idList )
{
if ( v.startsWith( QStringLiteral( "new" ) ) )
{
reloadData();
return true;
}
}
QStringList::const_iterator idIt = idList.constBegin();
featureIt = flist.begin();

Expand Down Expand Up @@ -1392,7 +1402,10 @@ bool QgsWFSProvider::sendTransactionDocument( const QDomDocument& doc, QDomDocum
QDomElement QgsWFSProvider::createTransactionElement( QDomDocument& doc ) const
{
QDomElement transactionElem = doc.createElementNS( QgsWFSConstants::WFS_NAMESPACE, QStringLiteral( "Transaction" ) );
transactionElem.setAttribute( QStringLiteral( "version" ), QStringLiteral( "1.0.0" ) );
// QString WfsVersion = mShared->mWFSVersion;
// For now: hardcoded to 1.0.0
QString WfsVersion = QStringLiteral( "1.0.0" );
transactionElem.setAttribute( QStringLiteral( "version" ), WfsVersion );
transactionElem.setAttribute( QStringLiteral( "service" ), QStringLiteral( "WFS" ) );
transactionElem.setAttribute( QStringLiteral( "xmlns:xsi" ), QStringLiteral( "http://www.w3.org/2001/XMLSchema-instance" ) );

Expand Down
1 change: 1 addition & 0 deletions src/providers/wfs/qgswfstransactionrequest.cpp
Expand Up @@ -36,6 +36,7 @@ bool QgsWFSTransactionRequest::send( const QDomDocument& doc, QDomDocument& serv
QgsDebugMsg( errorMsg );
return false;
}
QgsDebugMsg( mResponse );
return true;
}
return false;
Expand Down
136 changes: 136 additions & 0 deletions tests/src/python/test_provider_wfs.py
Expand Up @@ -2225,6 +2225,142 @@ def testDescribeFeatureTypeWithInlineType(self):
got = got_f[0].geometry().geometry()
self.assertEqual((got.x(), got.y()), (2.0, 49.0))

def testWFS20TransactionsDisabled(self):
"""Test WFS 2.0 Transaction disabled"""

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

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">
<ows:OperationsMetadata>
<ows:Operation name="GetFeature">
<ows:Constraint name="CountDefault">
<ows:NoValues/>
<ows:DefaultValue>1</ows:DefaultValue>
</ows:Constraint>
</ows:Operation>
<ows:Constraint name="ImplementsTransactionalWFS">
<ows:NoValues/>
<ows:DefaultValue>TRUE</ows:DefaultValue>
</ows:Constraint>
</ows:OperationsMetadata>
<FeatureTypeList>
<FeatureType>
<Name>my:typename</Name>
<Title>Title</Title>
<Abstract>Abstract</Abstract>
<DefaultCRS>urn:ogc:def:crs:EPSG::4326</DefaultCRS>
<ows:WGS84BoundingBox>
<ows:LowerCorner>-71.123 66.33</ows:LowerCorner>
<ows:UpperCorner>-65.32 78.3</ows:UpperCorner>
</ows:WGS84BoundingBox>
</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("""
<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(u"url='http://" + endpoint + u"' typename='my:typename'", u'test', u'WFS')
assert vl.isValid()
self.assertEqual(vl.dataProvider().capabilities() & vl.dataProvider().EditingCapabilities, vl.dataProvider().NoCapabilities)
self.assertEqual(vl.wkbType(), QgsWkbTypes.Point)

def testWFS20TransactionsEnabled(self):
"""Test WFS 2.0 Transaction enabled"""

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

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">
<ows:OperationsMetadata>
<ows:Operation name="GetFeature">
<ows:Constraint name="CountDefault">
<ows:NoValues/>
<ows:DefaultValue>1</ows:DefaultValue>
</ows:Constraint>
</ows:Operation>
<ows:Constraint name="ImplementsTransactionalWFS">
<ows:NoValues/>
<ows:DefaultValue>TRUE</ows:DefaultValue>
</ows:Constraint>
<ows:Operation name="Transaction">
<ows:DCP>
<ows:HTTP>
<ows:Get xlink:href="http://{endpoint}"/>
<ows:Post xlink:href="{endpoint}"/>
</ows:HTTP>
</ows:DCP>
<ows:Parameter name="inputFormat">
<ows:AllowedValues>
<ows:Value>text/xml; subtype=gml/3.2</ows:Value>
</ows:AllowedValues>
</ows:Parameter>
<ows:Parameter name="releaseAction">
<ows:AllowedValues>
<ows:Value>ALL</ows:Value>
<ows:Value>SOME</ows:Value>
</ows:AllowedValues>
</ows:Parameter>
</ows:Operation>
</ows:OperationsMetadata>
<FeatureTypeList>
<FeatureType>
<Name>my:typename</Name>
<Title>Title</Title>
<Abstract>Abstract</Abstract>
<DefaultCRS>urn:ogc:def:crs:EPSG::4326</DefaultCRS>
<ows:WGS84BoundingBox>
<ows:LowerCorner>-71.123 66.33</ows:LowerCorner>
<ows:UpperCorner>-65.32 78.3</ows:UpperCorner>
</ows:WGS84BoundingBox>
</FeatureType>
</FeatureTypeList>
</wfs:WFS_Capabilities>""".format(endpoint=endpoint).encode('UTF-8'))

with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAME=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="intfield" nillable="true" type="xsd:int"/>
<xsd:element maxOccurs="1" minOccurs="0" name="longfield" nillable="true" type="xsd:long"/>
<xsd:element maxOccurs="1" minOccurs="0" name="stringfield" nillable="true" type="xsd:string"/>
<xsd:element maxOccurs="1" minOccurs="0" name="datetimefield" nillable="true" type="xsd:dateTime"/>
<xsd:element maxOccurs="1" minOccurs="0" name="geomfield" 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(u"url='http://" + endpoint + u"' typename='my:typename'", u'test', u'WFS')
assert vl.isValid()
self.assertNotEqual(vl.dataProvider().capabilities() & vl.dataProvider().EditingCapabilities, vl.dataProvider().NoCapabilities)
self.assertEqual(vl.wkbType(), QgsWkbTypes.Point)

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

0 comments on commit 9324bda

Please sign in to comment.