Skip to content

Commit 9324bda

Browse files
committed
[bugfix] WFS-T Fixes #15597 #16043
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)
1 parent 6d23b04 commit 9324bda

4 files changed

Lines changed: 176 additions & 10 deletions

File tree

‎src/providers/wfs/qgswfscapabilities.cpp‎

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -246,11 +246,31 @@ void QgsWfsCapabilities::capabilitiesReplyFinished()
246246
}
247247

248248
// Parse operations supported for all feature types
249-
bool insertCap, updateCap, deleteCap;
250-
parseSupportedOperations( featureTypeListElem.firstChildElement( QStringLiteral( "Operations" ) ),
251-
insertCap,
252-
updateCap,
253-
deleteCap );
249+
bool insertCap = false;
250+
bool updateCap = false;
251+
bool deleteCap = false;
252+
// WFS < 2
253+
if ( mCaps.version.startsWith( QLatin1String( "1" ) ) )
254+
{
255+
parseSupportedOperations( featureTypeListElem.firstChildElement( QStringLiteral( "Operations" ) ),
256+
insertCap,
257+
updateCap,
258+
deleteCap );
259+
}
260+
else // WFS 2.0.0 tested on GeoServer
261+
{
262+
QDomNodeList operationNodes = doc.elementsByTagName( "Operation" );
263+
for ( int i = 0; i < operationNodes.count(); i++ )
264+
{
265+
QDomElement operationElement = operationNodes.at( i ).toElement( );
266+
if ( operationElement.isElement( ) && "Transaction" == operationElement.attribute( "name" ) )
267+
{
268+
insertCap = true;
269+
updateCap = true;
270+
deleteCap = true;
271+
}
272+
}
273+
}
254274

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

477-
// TODO: remove me when WFS-T 1.1 or 2.0 is done
478-
if ( !mCaps.version.startsWith( QLatin1String( "1.0" ) ) )
479-
return;
480-
481497
if ( operationsElem.isNull() )
482498
{
483499
return;

‎src/providers/wfs/qgswfsprovider.cpp‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,16 @@ bool QgsWFSProvider::addFeatures( QgsFeatureList &flist )
852852
{
853853
//transaction successful. Add the features to the cache
854854
QStringList idList = insertedFeatureIds( serverResponse );
855+
/* Fix issue with GeoServer and shapefile feature stores when no real
856+
feature id are returned but new0 returned instead of the featureId*/
857+
Q_FOREACH ( const QString &v, idList )
858+
{
859+
if ( v.startsWith( QStringLiteral( "new" ) ) )
860+
{
861+
reloadData();
862+
return true;
863+
}
864+
}
855865
QStringList::const_iterator idIt = idList.constBegin();
856866
featureIt = flist.begin();
857867

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

‎src/providers/wfs/qgswfstransactionrequest.cpp‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ bool QgsWFSTransactionRequest::send( const QDomDocument& doc, QDomDocument& serv
3636
QgsDebugMsg( errorMsg );
3737
return false;
3838
}
39+
QgsDebugMsg( mResponse );
3940
return true;
4041
}
4142
return false;

‎tests/src/python/test_provider_wfs.py‎

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2225,6 +2225,142 @@ def testDescribeFeatureTypeWithInlineType(self):
22252225
got = got_f[0].geometry().geometry()
22262226
self.assertEqual((got.x(), got.y()), (2.0, 49.0))
22272227

2228+
def testWFS20TransactionsDisabled(self):
2229+
"""Test WFS 2.0 Transaction disabled"""
2230+
2231+
endpoint = self.__class__.basetestpath + '/fake_qgis_http_endpoint_WFS_2.0_transaction'
2232+
2233+
with open(sanitize(endpoint, '?SERVICE=WFS?REQUEST=GetCapabilities?ACCEPTVERSIONS=2.0.0,1.1.0,1.0.0'), 'wb') as f:
2234+
f.write("""
2235+
<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">
2236+
<ows:OperationsMetadata>
2237+
<ows:Operation name="GetFeature">
2238+
<ows:Constraint name="CountDefault">
2239+
<ows:NoValues/>
2240+
<ows:DefaultValue>1</ows:DefaultValue>
2241+
</ows:Constraint>
2242+
</ows:Operation>
2243+
<ows:Constraint name="ImplementsTransactionalWFS">
2244+
<ows:NoValues/>
2245+
<ows:DefaultValue>TRUE</ows:DefaultValue>
2246+
</ows:Constraint>
2247+
</ows:OperationsMetadata>
2248+
<FeatureTypeList>
2249+
<FeatureType>
2250+
<Name>my:typename</Name>
2251+
<Title>Title</Title>
2252+
<Abstract>Abstract</Abstract>
2253+
<DefaultCRS>urn:ogc:def:crs:EPSG::4326</DefaultCRS>
2254+
<ows:WGS84BoundingBox>
2255+
<ows:LowerCorner>-71.123 66.33</ows:LowerCorner>
2256+
<ows:UpperCorner>-65.32 78.3</ows:UpperCorner>
2257+
</ows:WGS84BoundingBox>
2258+
</FeatureType>
2259+
</FeatureTypeList>
2260+
</wfs:WFS_Capabilities>""".encode('UTF-8'))
2261+
2262+
with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAME=my:typename'), 'wb') as f:
2263+
f.write("""
2264+
<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">
2265+
<xsd:import namespace="http://www.opengis.net/gml/3.2"/>
2266+
<xsd:complexType name="typenameType">
2267+
<xsd:complexContent>
2268+
<xsd:extension base="gml:AbstractFeatureType">
2269+
<xsd:sequence>
2270+
<xsd:element maxOccurs="1" minOccurs="0" name="geometryProperty" nillable="true" type="gml:PointPropertyType"/>
2271+
</xsd:sequence>
2272+
</xsd:extension>
2273+
</xsd:complexContent>
2274+
</xsd:complexType>
2275+
<xsd:element name="typename" substitutionGroup="gml:_Feature" type="my:typenameType"/>
2276+
</xsd:schema>
2277+
""".encode('UTF-8'))
2278+
2279+
# Create test layer
2280+
vl = QgsVectorLayer(u"url='http://" + endpoint + u"' typename='my:typename'", u'test', u'WFS')
2281+
assert vl.isValid()
2282+
self.assertEqual(vl.dataProvider().capabilities() & vl.dataProvider().EditingCapabilities, vl.dataProvider().NoCapabilities)
2283+
self.assertEqual(vl.wkbType(), QgsWkbTypes.Point)
2284+
2285+
def testWFS20TransactionsEnabled(self):
2286+
"""Test WFS 2.0 Transaction enabled"""
2287+
2288+
endpoint = self.__class__.basetestpath + '/fake_qgis_http_endpoint_WFS_2.0_transaction'
2289+
2290+
with open(sanitize(endpoint, '?SERVICE=WFS?REQUEST=GetCapabilities?ACCEPTVERSIONS=2.0.0,1.1.0,1.0.0'), 'wb') as f:
2291+
f.write("""
2292+
<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">
2293+
<ows:OperationsMetadata>
2294+
<ows:Operation name="GetFeature">
2295+
<ows:Constraint name="CountDefault">
2296+
<ows:NoValues/>
2297+
<ows:DefaultValue>1</ows:DefaultValue>
2298+
</ows:Constraint>
2299+
</ows:Operation>
2300+
<ows:Constraint name="ImplementsTransactionalWFS">
2301+
<ows:NoValues/>
2302+
<ows:DefaultValue>TRUE</ows:DefaultValue>
2303+
</ows:Constraint>
2304+
<ows:Operation name="Transaction">
2305+
<ows:DCP>
2306+
<ows:HTTP>
2307+
<ows:Get xlink:href="http://{endpoint}"/>
2308+
<ows:Post xlink:href="{endpoint}"/>
2309+
</ows:HTTP>
2310+
</ows:DCP>
2311+
<ows:Parameter name="inputFormat">
2312+
<ows:AllowedValues>
2313+
<ows:Value>text/xml; subtype=gml/3.2</ows:Value>
2314+
</ows:AllowedValues>
2315+
</ows:Parameter>
2316+
<ows:Parameter name="releaseAction">
2317+
<ows:AllowedValues>
2318+
<ows:Value>ALL</ows:Value>
2319+
<ows:Value>SOME</ows:Value>
2320+
</ows:AllowedValues>
2321+
</ows:Parameter>
2322+
</ows:Operation>
2323+
</ows:OperationsMetadata>
2324+
<FeatureTypeList>
2325+
<FeatureType>
2326+
<Name>my:typename</Name>
2327+
<Title>Title</Title>
2328+
<Abstract>Abstract</Abstract>
2329+
<DefaultCRS>urn:ogc:def:crs:EPSG::4326</DefaultCRS>
2330+
<ows:WGS84BoundingBox>
2331+
<ows:LowerCorner>-71.123 66.33</ows:LowerCorner>
2332+
<ows:UpperCorner>-65.32 78.3</ows:UpperCorner>
2333+
</ows:WGS84BoundingBox>
2334+
</FeatureType>
2335+
</FeatureTypeList>
2336+
</wfs:WFS_Capabilities>""".format(endpoint=endpoint).encode('UTF-8'))
2337+
2338+
with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAME=my:typename'), 'wb') as f:
2339+
f.write("""
2340+
<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">
2341+
<xsd:import namespace="http://www.opengis.net/gml/3.2"/>
2342+
<xsd:complexType name="typenameType">
2343+
<xsd:complexContent>
2344+
<xsd:extension base="gml:AbstractFeatureType">
2345+
<xsd:sequence>
2346+
<xsd:element maxOccurs="1" minOccurs="0" name="intfield" nillable="true" type="xsd:int"/>
2347+
<xsd:element maxOccurs="1" minOccurs="0" name="longfield" nillable="true" type="xsd:long"/>
2348+
<xsd:element maxOccurs="1" minOccurs="0" name="stringfield" nillable="true" type="xsd:string"/>
2349+
<xsd:element maxOccurs="1" minOccurs="0" name="datetimefield" nillable="true" type="xsd:dateTime"/>
2350+
<xsd:element maxOccurs="1" minOccurs="0" name="geomfield" nillable="true" type="gml:PointPropertyType"/>
2351+
</xsd:sequence>
2352+
</xsd:extension>
2353+
</xsd:complexContent>
2354+
</xsd:complexType>
2355+
<xsd:element name="typename" substitutionGroup="gml:_Feature" type="my:typenameType"/>
2356+
</xsd:schema>
2357+
""".encode('UTF-8'))
2358+
2359+
# Create test layer
2360+
vl = QgsVectorLayer(u"url='http://" + endpoint + u"' typename='my:typename'", u'test', u'WFS')
2361+
assert vl.isValid()
2362+
self.assertNotEqual(vl.dataProvider().capabilities() & vl.dataProvider().EditingCapabilities, vl.dataProvider().NoCapabilities)
2363+
self.assertEqual(vl.wkbType(), QgsWkbTypes.Point)
22282364

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

0 commit comments

Comments
 (0)