mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-13 00:03:09 -04:00
QGIS Server WFS3 ACL + alias + excluded fields
Takes into account aliases, excluded attributes and ACL plugins
This commit is contained in:
parent
b2c7ba05c2
commit
67c8e56f9c
@ -39,14 +39,6 @@ Parses a comma separated ``bbox`` into a (possibily empty) :py:class:`QgsRectang
|
||||
static QgsCoordinateReferenceSystem parseCrs( const QString &bboxCrs );
|
||||
%Docstring
|
||||
Parses the CRS URI ``bboxCrs`` (example: "http://www.opengis.net/def/crs/OGC/1.3/CRS84") into a QGIS CRS object
|
||||
%End
|
||||
|
||||
static const QgsFields publishedFields( const QgsVectorLayer *layer );
|
||||
%Docstring
|
||||
Returns the list of fields accessible to the service for a given ``layer``.
|
||||
|
||||
This method takes into account the ACL restrictions provided by QGIS Server Access Control plugins.
|
||||
TODO: implement ACL
|
||||
%End
|
||||
|
||||
static const QVector<QgsMapLayer *> publishedWfsLayers( const QgsProject *project );
|
||||
|
@ -91,12 +91,6 @@ QgsCoordinateReferenceSystem QgsServerApiUtils::parseCrs( const QString &bboxCrs
|
||||
}
|
||||
}
|
||||
|
||||
const QgsFields QgsServerApiUtils::publishedFields( const QgsVectorLayer *layer )
|
||||
{
|
||||
// TODO: implement plugin's ACL filtering
|
||||
return layer->fields();
|
||||
}
|
||||
|
||||
const QVector<QgsMapLayer *> QgsServerApiUtils::publishedWfsLayers( const QgsProject *project )
|
||||
{
|
||||
const QStringList wfsLayerIds = QgsServerProjectUtils::wfsLayerIds( *project );
|
||||
|
@ -71,14 +71,6 @@ class SERVER_EXPORT QgsServerApiUtils
|
||||
*/
|
||||
static QgsCoordinateReferenceSystem parseCrs( const QString &bboxCrs );
|
||||
|
||||
/**
|
||||
* Returns the list of fields accessible to the service for a given \a layer.
|
||||
*
|
||||
* This method takes into account the ACL restrictions provided by QGIS Server Access Control plugins.
|
||||
* TODO: implement ACL
|
||||
*/
|
||||
static const QgsFields publishedFields( const QgsVectorLayer *layer );
|
||||
|
||||
/**
|
||||
* Returns the list of layers accessible to the service for a given \a project.
|
||||
*
|
||||
|
@ -173,6 +173,7 @@ void QgsServerOgcApiHandler::jsonDump( json &data, const QgsServerApiContext &co
|
||||
QDateTime time { QDateTime::currentDateTime() };
|
||||
time.setTimeSpec( Qt::TimeSpec::UTC );
|
||||
data["timeStamp"] = time.toString( Qt::DateFormat::ISODate ).toStdString() ;
|
||||
context.response()->setStatusCode( 200 );
|
||||
context.response()->setHeader( QStringLiteral( "Content-Type" ), contentType );
|
||||
#ifdef QGISDEBUG
|
||||
context.response()->write( data.dump( 2 ) );
|
||||
|
@ -33,6 +33,7 @@
|
||||
|
||||
#ifdef HAVE_SERVER_PYTHON_PLUGINS
|
||||
#include "qgsfilterrestorer.h"
|
||||
#include "qgsaccesscontrol.h"
|
||||
#endif
|
||||
|
||||
#include <QMimeDatabase>
|
||||
@ -202,69 +203,71 @@ void QgsWfs3AbstractItemsHandler::checkLayerIsAccessible( const QgsVectorLayer *
|
||||
}
|
||||
}
|
||||
|
||||
QgsFeatureRequest QgsWfs3AbstractItemsHandler::filteredRequest( const QgsMapLayer *layer, const QgsServerApiContext &context ) const
|
||||
QgsFeatureRequest QgsWfs3AbstractItemsHandler::filteredRequest( const QgsVectorLayer *vLayer, const QgsServerApiContext &context ) const
|
||||
{
|
||||
QgsFeatureRequest featureRequest;
|
||||
QgsExpressionContext expressionContext;
|
||||
expressionContext << QgsExpressionContextUtils::globalScope()
|
||||
<< QgsExpressionContextUtils::projectScope( context.project() )
|
||||
<< QgsExpressionContextUtils::layerScope( layer );
|
||||
<< QgsExpressionContextUtils::layerScope( vLayer );
|
||||
|
||||
featureRequest.setExpressionContext( expressionContext );
|
||||
|
||||
//is there alias info for this vector layer?
|
||||
const QgsVectorLayer *vLayer = static_cast<const QgsVectorLayer *>( layer );
|
||||
QMap< int, QString > layerAliasInfo;
|
||||
const QgsStringMap aliasMap = vLayer->attributeAliases();
|
||||
for ( const auto &aliasKey : aliasMap.keys() )
|
||||
{
|
||||
int attrIndex = vLayer->fields().lookupField( aliasKey );
|
||||
if ( attrIndex != -1 )
|
||||
{
|
||||
layerAliasInfo.insert( attrIndex, aliasMap.value( aliasKey ) );
|
||||
}
|
||||
}
|
||||
|
||||
QgsAttributeList attrIndexes = vLayer->attributeList();
|
||||
|
||||
// Removed attributes
|
||||
//excluded attributes for this layer
|
||||
const QSet<QString> &layerExcludedAttributes = vLayer->excludeAttributesWfs();
|
||||
if ( !attrIndexes.isEmpty() && !layerExcludedAttributes.isEmpty() )
|
||||
{
|
||||
const QgsFields &fields = vLayer->fields();
|
||||
for ( const QString &excludedAttribute : layerExcludedAttributes )
|
||||
{
|
||||
int fieldNameIdx = fields.indexOf( excludedAttribute );
|
||||
if ( fieldNameIdx > -1 && attrIndexes.contains( fieldNameIdx ) )
|
||||
{
|
||||
attrIndexes.removeOne( fieldNameIdx );
|
||||
}
|
||||
}
|
||||
}
|
||||
featureRequest.setSubsetOfAttributes( attrIndexes );
|
||||
|
||||
#ifdef HAVE_SERVER_PYTHON_PLUGINS
|
||||
// Python plugins can make further modifications to the allowed attributes
|
||||
QgsAccessControl *accessControl = context.serverInterface()->accessControls();
|
||||
if ( accessControl )
|
||||
{
|
||||
accessControl->filterFeatures( vLayer, featureRequest );
|
||||
|
||||
QStringList attributes = QStringList();
|
||||
for ( int idx : attrIndexes )
|
||||
{
|
||||
attributes.append( vLayer->fields().field( idx ).name() );
|
||||
}
|
||||
featureRequest.setSubsetOfAttributes(
|
||||
accessControl->layerAttributes( vLayer, attributes ),
|
||||
vLayer->fields() );
|
||||
}
|
||||
#endif
|
||||
|
||||
QSet<QString> publishedAttrs;
|
||||
const QgsFields constFields { publishedFields( vLayer, context ) };
|
||||
for ( const QgsField &f : constFields )
|
||||
{
|
||||
publishedAttrs.insert( f.name() );
|
||||
}
|
||||
featureRequest.setSubsetOfAttributes( publishedAttrs, vLayer->fields() );
|
||||
return featureRequest;
|
||||
}
|
||||
|
||||
QgsFields QgsWfs3AbstractItemsHandler::publishedFields( const QgsVectorLayer *vLayer, const QgsServerApiContext &context ) const
|
||||
{
|
||||
|
||||
QStringList publishedAttributes = QStringList();
|
||||
// Removed attributes
|
||||
// WFS excluded attributes for this layer
|
||||
const QSet<QString> &layerExcludedAttributes = vLayer->excludeAttributesWfs();
|
||||
const QgsFields &fields = vLayer->fields();
|
||||
for ( int i = 0; i < fields.count(); ++i )
|
||||
{
|
||||
if ( ! layerExcludedAttributes.contains( fields.at( i ).name() ) )
|
||||
{
|
||||
publishedAttributes.push_back( fields.at( i ).name() );
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_SERVER_PYTHON_PLUGINS
|
||||
// Python plugins can make further modifications to the allowed attributes
|
||||
QgsAccessControl *accessControl = context.serverInterface()->accessControls();
|
||||
if ( accessControl )
|
||||
{
|
||||
publishedAttributes = accessControl->layerAttributes( vLayer, publishedAttributes );
|
||||
}
|
||||
#endif
|
||||
|
||||
QgsFields publishedFields;
|
||||
for ( int i = 0; i < fields.count(); ++i )
|
||||
{
|
||||
if ( publishedAttributes.contains( fields.at( i ).name() ) )
|
||||
{
|
||||
publishedFields.append( fields.at( i ) );
|
||||
}
|
||||
}
|
||||
return publishedFields;
|
||||
}
|
||||
|
||||
QgsWfs3LandingPageHandler::QgsWfs3LandingPageHandler()
|
||||
{
|
||||
}
|
||||
@ -765,7 +768,8 @@ QList<QgsServerQueryStringParameter> QgsWfs3CollectionsItemsHandler::parameters(
|
||||
} );
|
||||
offset.setDescription( QStringLiteral( "Offset for features to retrieve [0-%1]" ).arg( mapLayer->featureCount( ) ) );
|
||||
offsetValidatorSet = true;
|
||||
for ( const auto &p : fieldParameters( mapLayer ) )
|
||||
const QList<QgsServerQueryStringParameter> constFieldParameters { fieldParameters( mapLayer, context ) };
|
||||
for ( const auto &p : constFieldParameters )
|
||||
{
|
||||
params.push_back( p );
|
||||
}
|
||||
@ -845,7 +849,8 @@ json QgsWfs3CollectionsItemsHandler::schema( const QgsServerApiContext &context
|
||||
}
|
||||
};
|
||||
|
||||
for ( const auto &p : fieldParameters( mapLayer ) )
|
||||
const QList<QgsServerQueryStringParameter> constFieldParameters { fieldParameters( mapLayer, context ) };
|
||||
for ( const auto &p : constFieldParameters )
|
||||
{
|
||||
const std::string name { p.name().toStdString() };
|
||||
parameters.push_back( p.data() );
|
||||
@ -899,14 +904,15 @@ json QgsWfs3CollectionsItemsHandler::schema( const QgsServerApiContext &context
|
||||
return data;
|
||||
}
|
||||
|
||||
const QList<QgsServerQueryStringParameter> QgsWfs3CollectionsItemsHandler::fieldParameters( const QgsVectorLayer *mapLayer ) const
|
||||
const QList<QgsServerQueryStringParameter> QgsWfs3CollectionsItemsHandler::fieldParameters( const QgsVectorLayer *mapLayer, const QgsServerApiContext &context ) const
|
||||
{
|
||||
QList<QgsServerQueryStringParameter> params;
|
||||
if ( mapLayer )
|
||||
{
|
||||
const QgsFields constFields { QgsServerApiUtils::publishedFields( mapLayer ) };
|
||||
const QgsFields constFields { publishedFields( mapLayer, context ) };
|
||||
for ( const auto &f : constFields )
|
||||
{
|
||||
const QString fName { f.alias().isEmpty() ? f.name() : f.alias() };
|
||||
QgsServerQueryStringParameter::Type t;
|
||||
switch ( f.type() )
|
||||
{
|
||||
@ -922,8 +928,8 @@ const QList<QgsServerQueryStringParameter> QgsWfs3CollectionsItemsHandler::field
|
||||
t = QgsServerQueryStringParameter::Type::String;
|
||||
break;
|
||||
}
|
||||
QgsServerQueryStringParameter fieldParam { f.name(), false,
|
||||
t, QStringLiteral( "Retrieve features filtered by: %1 (%2)" ).arg( f.name() )
|
||||
QgsServerQueryStringParameter fieldParam { fName, false,
|
||||
t, QStringLiteral( "Retrieve features filtered by: %1 (%2)" ).arg( fName )
|
||||
.arg( QgsServerQueryStringParameter::typeName( t ) ) };
|
||||
params.push_back( fieldParam );
|
||||
}
|
||||
@ -987,10 +993,11 @@ void QgsWfs3CollectionsItemsHandler::handleRequest( const QgsServerApiContext &c
|
||||
|
||||
// Attribute filters
|
||||
QgsStringMap attrFilters;
|
||||
const QgsFields constField { QgsServerApiUtils::publishedFields( mapLayer ) };
|
||||
for ( const QgsField &f : constField )
|
||||
const QgsFields constFields { publishedFields( mapLayer, context ) };
|
||||
for ( const QgsField &f : constFields )
|
||||
{
|
||||
const QString val = params.value( f.name() ).toString() ;
|
||||
const QString fName { f.alias().isEmpty() ? f.name() : f.alias() };
|
||||
const QString val = params.value( fName ).toString() ;
|
||||
if ( ! val.isEmpty() )
|
||||
{
|
||||
QString sanitized { QgsServerApiUtils::sanitizedFieldValue( val ) };
|
||||
@ -998,7 +1005,7 @@ void QgsWfs3CollectionsItemsHandler::handleRequest( const QgsServerApiContext &c
|
||||
{
|
||||
throw QgsServerApiBadRequestException( QStringLiteral( "Invalid filter field value [%1=%2]" ).arg( f.name() ).arg( val ) );
|
||||
}
|
||||
attrFilters[f.name()] = sanitized;
|
||||
attrFilters[fName] = sanitized;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1018,18 +1025,22 @@ void QgsWfs3CollectionsItemsHandler::handleRequest( const QgsServerApiContext &c
|
||||
}
|
||||
|
||||
// Inputs are valid, process request
|
||||
QgsFeatureRequest req;
|
||||
QgsFeatureRequest featureRequest = filteredRequest( mapLayer, context );
|
||||
if ( ! filterRect.isNull() )
|
||||
{
|
||||
QgsCoordinateTransform ct( bboxCrs, mapLayer->crs(), context.project()->transformContext() );
|
||||
ct.transform( filterRect );
|
||||
req.setFilterRect( ct.transform( filterRect ) );
|
||||
featureRequest.setFilterRect( ct.transform( filterRect ) );
|
||||
}
|
||||
|
||||
QString filterExpression;
|
||||
if ( ! attrFilters.isEmpty() )
|
||||
{
|
||||
QStringList expressions;
|
||||
if ( featureRequest.filterExpression() && ! featureRequest.filterExpression()->expression().isEmpty() )
|
||||
{
|
||||
expressions.push_back( featureRequest.filterExpression()->expression() );
|
||||
}
|
||||
for ( auto it = attrFilters.constBegin(); it != attrFilters.constEnd(); it++ )
|
||||
{
|
||||
// Handle star
|
||||
@ -1045,17 +1056,19 @@ void QgsWfs3CollectionsItemsHandler::handleRequest( const QgsServerApiContext &c
|
||||
}
|
||||
}
|
||||
filterExpression = expressions.join( QStringLiteral( " AND " ) );
|
||||
req.setFilterExpression( filterExpression );
|
||||
featureRequest.setFilterExpression( filterExpression );
|
||||
}
|
||||
|
||||
// WFS3 core specs only serves 4326
|
||||
req.setDestinationCrs( crs, context.project()->transformContext() );
|
||||
// Add offset to limit because paging is not supported from QgsFeatureRequest
|
||||
req.setLimit( limit + offset );
|
||||
featureRequest.setDestinationCrs( crs, context.project()->transformContext() );
|
||||
// Add offset to limit because paging is not supported by QgsFeatureRequest
|
||||
featureRequest.setLimit( limit + offset );
|
||||
QgsJsonExporter exporter { mapLayer };
|
||||
exporter.setAttributes( featureRequest.subsetOfAttributes() );
|
||||
exporter.setAttributeDisplayName( true );
|
||||
exporter.setSourceCrs( mapLayer->crs() );
|
||||
QgsFeatureList featureList;
|
||||
QgsFeatureIterator features { mapLayer->getFeatures( req ) };
|
||||
QgsFeatureIterator features { mapLayer->getFeatures( featureRequest ) };
|
||||
QgsFeature feat;
|
||||
long i { 0 };
|
||||
while ( features.nextFeature( feat ) )
|
||||
@ -1076,11 +1089,11 @@ void QgsWfs3CollectionsItemsHandler::handleRequest( const QgsServerApiContext &c
|
||||
{
|
||||
if ( filterExpression.isEmpty() )
|
||||
{
|
||||
req.setNoAttributes();
|
||||
featureRequest.setNoAttributes();
|
||||
}
|
||||
req.setFlags( QgsFeatureRequest::Flag::NoGeometry );
|
||||
req.setLimit( -1 );
|
||||
features = mapLayer->getFeatures( req );
|
||||
featureRequest.setFlags( QgsFeatureRequest::Flag::NoGeometry );
|
||||
featureRequest.setLimit( -1 );
|
||||
features = mapLayer->getFeatures( featureRequest );
|
||||
while ( features.nextFeature( feat ) )
|
||||
{
|
||||
matchedFeaturesCount++;
|
||||
@ -1191,7 +1204,6 @@ void QgsWfs3CollectionsFeatureHandler::handleRequest( const QgsServerApiContext
|
||||
if ( context.request()->method() == QgsServerRequest::Method::GetMethod )
|
||||
{
|
||||
const QString featureId { match.captured( QStringLiteral( "featureId" ) ) };
|
||||
QgsJsonExporter exporter { mapLayer };
|
||||
|
||||
#ifdef HAVE_SERVER_PYTHON_PLUGINS
|
||||
QgsAccessControl *accessControl = context.serverInterface()->accessControls();
|
||||
@ -1213,6 +1225,9 @@ void QgsWfs3CollectionsFeatureHandler::handleRequest( const QgsServerApiContext
|
||||
QgsServerApiInternalServerError( QStringLiteral( "Invalid feature [%1]" ).arg( featureId ) );
|
||||
}
|
||||
|
||||
QgsJsonExporter exporter { mapLayer };
|
||||
exporter.setAttributes( featureRequest.subsetOfAttributes() );
|
||||
exporter.setAttributeDisplayName( true );
|
||||
json data = exporter.exportFeatureToJsonObject( feature );
|
||||
data["links"] = links( context );
|
||||
json navigation = json::array();
|
||||
|
@ -19,6 +19,7 @@
|
||||
#define QGS_WFS3_HANDLERS_H
|
||||
|
||||
#include "qgsserverogcapihandler.h"
|
||||
#include "qgsfields.h"
|
||||
|
||||
class QgsFeatureRequest;
|
||||
class QgsServerOgcApi;
|
||||
@ -42,7 +43,22 @@ class QgsWfs3AbstractItemsHandler: public QgsServerOgcApiHandler
|
||||
*/
|
||||
void checkLayerIsAccessible( const QgsVectorLayer *layer, const QgsServerApiContext &context ) const;
|
||||
|
||||
QgsFeatureRequest filteredRequest( const QgsMapLayer *layer, const QgsServerApiContext &context ) const;
|
||||
/**
|
||||
* Creates a filtered QgsFeatureRequest containing only fields published for WMS and plugin filters applied.
|
||||
* \param layer the vector layer
|
||||
* \param context the server api context
|
||||
* \return QgsFeatureRequest with filters applied
|
||||
*/
|
||||
QgsFeatureRequest filteredRequest( const QgsVectorLayer *layer, const QgsServerApiContext &context ) const;
|
||||
|
||||
/**
|
||||
* Returns a filtered list of fields containing only fields published for WMS and plugin filters applied.
|
||||
* @param layer the vector layer
|
||||
* @param context the server api context
|
||||
* @return QgsFields list with filters applied
|
||||
*/
|
||||
QgsFields publishedFields( const QgsVectorLayer *layer, const QgsServerApiContext &context ) const;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
@ -224,7 +240,7 @@ class QgsWfs3CollectionsItemsHandler: public QgsWfs3AbstractItemsHandler
|
||||
private:
|
||||
|
||||
// Retrieve the fields filter parameters
|
||||
const QList<QgsServerQueryStringParameter> fieldParameters( const QgsVectorLayer *mapLayer ) const;
|
||||
const QList<QgsServerQueryStringParameter> fieldParameters( const QgsVectorLayer *mapLayer, const QgsServerApiContext &context ) const;
|
||||
};
|
||||
|
||||
|
||||
|
@ -118,7 +118,7 @@ class QgsServerAPITestBase(QgsServerTestBase):
|
||||
""" QGIS API server tests"""
|
||||
|
||||
# Set to True in child classes to re-generate reference files for this class
|
||||
regeregenerate_api_reference = True
|
||||
regeregenerate_api_reference = False
|
||||
|
||||
def dump(self, response):
|
||||
"""Returns the response body as str"""
|
||||
@ -417,17 +417,26 @@ class QgsServerAPITest(QgsServerAPITestBase):
|
||||
request = QgsBufferServerRequest('http://server.qgis.org/wfs3/collections/testlayer3/items?name=two')
|
||||
self.server.handleRequest(request, response, project)
|
||||
self.assertEqual(response.statusCode(), 404) # Not found
|
||||
request = QgsBufferServerRequest('http://server.qgis.org/wfs3/collections/layer_with_short_name/items?name=two')
|
||||
request = QgsBufferServerRequest('http://server.qgis.org/wfs3/collections/layer1_with_short_name/items?name=two')
|
||||
self.server.handleRequest(request, response, project)
|
||||
self.assertEqual(response.statusCode(), 200)
|
||||
self.compareApi(request, project, 'test_wfs3_collections_items_testlayer_with_short_name_eq_two.json')
|
||||
self.compareApi(request, project, 'test_wfs3_collections_items_layer1_with_short_name_eq_two.json')
|
||||
|
||||
def test_wfs3_field_filters_star(self):
|
||||
"""Test field filters"""
|
||||
project = QgsProject()
|
||||
project.read(unitTestDataPath('qgis_server') + '/test_project_api.qgs')
|
||||
request = QgsBufferServerRequest('http://server.qgis.org/wfs3/collections/testlayer_with_short_name/items?name=tw*')
|
||||
self.compareApi(request, project, 'test_wfs3_collections_items_testlayer_with_short_name_eq_two_star.json')
|
||||
request = QgsBufferServerRequest('http://server.qgis.org/wfs3/collections/layer1_with_short_name/items?name=tw*')
|
||||
response = self.compareApi(request, project, 'test_wfs3_collections_items_layer1_with_short_name_eq_tw_star.json')
|
||||
self.assertEqual(response.statusCode(), 200)
|
||||
|
||||
def test_wfs3_excluded_attributes(self):
|
||||
"""Test excluded attributes"""
|
||||
project = QgsProject()
|
||||
project.read(unitTestDataPath('qgis_server') + '/test_project_api.qgs')
|
||||
request = QgsBufferServerRequest('http://server.qgis.org/wfs3/collections/exclude_attribute/items/0.geojson')
|
||||
response = self.compareApi(request, project, 'test_wfs3_collections_items_exclude_attribute_0.json')
|
||||
self.assertEqual(response.statusCode(), 200)
|
||||
|
||||
|
||||
class Handler1(QgsServerOgcApiHandler):
|
||||
|
@ -617,17 +617,6 @@ Content-Type: application/openapi+json;version=3.0
|
||||
},
|
||||
"style": "form"
|
||||
},
|
||||
{
|
||||
"description": "Filter the collection by 'name'",
|
||||
"explode": false,
|
||||
"in": "query",
|
||||
"name": "name",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"style": "form"
|
||||
},
|
||||
{
|
||||
"description": "Filter the collection by 'utf8nameè'",
|
||||
"explode": false,
|
||||
@ -704,10 +693,10 @@ Content-Type: application/openapi+json;version=3.0
|
||||
}
|
||||
],
|
||||
{
|
||||
"description": "Filter the collection by 'id'",
|
||||
"description": "Filter the collection by 'alias_id'",
|
||||
"explode": false,
|
||||
"in": "query",
|
||||
"name": "id",
|
||||
"name": "alias_id",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
@ -715,10 +704,10 @@ Content-Type: application/openapi+json;version=3.0
|
||||
"style": "form"
|
||||
},
|
||||
{
|
||||
"description": "Filter the collection by 'name'",
|
||||
"description": "Filter the collection by 'alias_name'",
|
||||
"explode": false,
|
||||
"in": "query",
|
||||
"name": "name",
|
||||
"name": "alias_name",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
@ -1438,5 +1427,5 @@ Content-Type: application/openapi+json;version=3.0
|
||||
"name": "Features"
|
||||
}
|
||||
],
|
||||
"timeStamp": "2019-09-08T18:57:09Z"
|
||||
"timeStamp": "2019-09-10T18:18:14Z"
|
||||
}
|
@ -19,5 +19,5 @@ Content-Type: application/json
|
||||
"type": "text/html"
|
||||
}
|
||||
],
|
||||
"timeStamp": "2019-09-08T18:57:10Z"
|
||||
"timeStamp": "2019-09-10T18:18:15Z"
|
||||
}
|
32
tests/testdata/qgis_server/api/test_wfs3_collections_items_exclude_attribute_0.json
vendored
Normal file
32
tests/testdata/qgis_server/api/test_wfs3_collections_items_exclude_attribute_0.json
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
Content-Type: application/geo+json
|
||||
|
||||
{
|
||||
"geometry": {
|
||||
"coordinates": [
|
||||
8.203496,
|
||||
44.901483
|
||||
],
|
||||
"type": "Point"
|
||||
},
|
||||
"id": 0,
|
||||
"links": [
|
||||
{
|
||||
"href": "http://server.qgis.org/wfs3/collections/exclude_attribute/items/0.geojson",
|
||||
"rel": "self",
|
||||
"title": "Retrieve a feature as GEOJSON",
|
||||
"type": "application/geo+json"
|
||||
},
|
||||
{
|
||||
"href": "http://server.qgis.org/wfs3/collections/exclude_attribute/items/0.html",
|
||||
"rel": "alternate",
|
||||
"title": "Retrieve a feature as HTML",
|
||||
"type": "text/html"
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"id": 1,
|
||||
"utf8nameè": "one èé"
|
||||
},
|
||||
"timeStamp": "2019-09-10T18:18:16Z",
|
||||
"type": "Feature"
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
Content-Type: application/geo+json
|
||||
|
||||
{
|
||||
"features": [
|
||||
{
|
||||
"geometry": {
|
||||
"coordinates": [
|
||||
8.203547,
|
||||
44.901436
|
||||
],
|
||||
"type": "Point"
|
||||
},
|
||||
"id": 1,
|
||||
"properties": {
|
||||
"id": 2,
|
||||
"name": "two",
|
||||
"utf8nameè": "two àò"
|
||||
},
|
||||
"type": "Feature"
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"href": "http://server.qgis.org/wfs3/collections/layer1_with_short_name/items.geojson?name=tw*",
|
||||
"rel": "self",
|
||||
"title": "Retrieve the features of the collection as GEOJSON",
|
||||
"type": "application/geo+json"
|
||||
},
|
||||
{
|
||||
"href": "http://server.qgis.org/wfs3/collections/layer1_with_short_name/items.html?name=tw*",
|
||||
"rel": "alternate",
|
||||
"title": "Retrieve the features of the collection as HTML",
|
||||
"type": "text/html"
|
||||
}
|
||||
],
|
||||
"numberMatched": 1,
|
||||
"numberReturned": 1,
|
||||
"timeStamp": "2019-09-10T18:18:16Z",
|
||||
"type": "FeatureCollection"
|
||||
}
|
40
tests/testdata/qgis_server/api/test_wfs3_collections_items_layer1_with_short_name_eq_two.json
vendored
Normal file
40
tests/testdata/qgis_server/api/test_wfs3_collections_items_layer1_with_short_name_eq_two.json
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
Content-Type: application/geo+json
|
||||
|
||||
{
|
||||
"features": [
|
||||
{
|
||||
"geometry": {
|
||||
"coordinates": [
|
||||
8.203547,
|
||||
44.901436
|
||||
],
|
||||
"type": "Point"
|
||||
},
|
||||
"id": 1,
|
||||
"properties": {
|
||||
"id": 2,
|
||||
"name": "two",
|
||||
"utf8nameè": "two àò"
|
||||
},
|
||||
"type": "Feature"
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"href": "http://server.qgis.org/wfs3/collections/layer1_with_short_name/items.geojson?name=two",
|
||||
"rel": "self",
|
||||
"title": "Retrieve the features of the collection as GEOJSON",
|
||||
"type": "application/geo+json"
|
||||
},
|
||||
{
|
||||
"href": "http://server.qgis.org/wfs3/collections/layer1_with_short_name/items.html?name=two",
|
||||
"rel": "alternate",
|
||||
"title": "Retrieve the features of the collection as HTML",
|
||||
"type": "text/html"
|
||||
}
|
||||
],
|
||||
"numberMatched": 1,
|
||||
"numberReturned": 1,
|
||||
"timeStamp": "2019-09-10T18:18:16Z",
|
||||
"type": "FeatureCollection"
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
Content-Type: application/json
|
||||
|
||||
[{"code":"API not found error","description":"Collection with given id (testlayer_with_short_name) was not found or multiple matches were found"}]
|
@ -67,6 +67,6 @@ Content-Type: application/geo+json
|
||||
],
|
||||
"numberMatched": 3,
|
||||
"numberReturned": 3,
|
||||
"timeStamp": "2019-09-08T18:57:09Z",
|
||||
"timeStamp": "2019-09-10T18:18:14Z",
|
||||
"type": "FeatureCollection"
|
||||
}
|
@ -35,6 +35,6 @@ Content-Type: application/geo+json
|
||||
],
|
||||
"numberMatched": 1,
|
||||
"numberReturned": 1,
|
||||
"timeStamp": "2019-09-08T18:57:09Z",
|
||||
"timeStamp": "2019-09-10T18:18:15Z",
|
||||
"type": "FeatureCollection"
|
||||
}
|
@ -51,6 +51,6 @@ Content-Type: application/geo+json
|
||||
],
|
||||
"numberMatched": 2,
|
||||
"numberReturned": 2,
|
||||
"timeStamp": "2019-09-08T18:57:09Z",
|
||||
"timeStamp": "2019-09-10T18:18:15Z",
|
||||
"type": "FeatureCollection"
|
||||
}
|
@ -67,6 +67,6 @@ Content-Type: application/geo+json
|
||||
],
|
||||
"numberMatched": 3,
|
||||
"numberReturned": 3,
|
||||
"timeStamp": "2019-09-08T18:57:09Z",
|
||||
"timeStamp": "2019-09-10T18:18:15Z",
|
||||
"type": "FeatureCollection"
|
||||
}
|
@ -42,6 +42,6 @@ Content-Type: application/geo+json
|
||||
],
|
||||
"numberMatched": 3,
|
||||
"numberReturned": 1,
|
||||
"timeStamp": "2019-09-08T18:57:10Z",
|
||||
"timeStamp": "2019-09-10T18:18:15Z",
|
||||
"type": "FeatureCollection"
|
||||
}
|
@ -49,6 +49,6 @@ Content-Type: application/geo+json
|
||||
],
|
||||
"numberMatched": 3,
|
||||
"numberReturned": 1,
|
||||
"timeStamp": "2019-09-08T18:57:10Z",
|
||||
"timeStamp": "2019-09-10T18:18:15Z",
|
||||
"type": "FeatureCollection"
|
||||
}
|
@ -162,5 +162,5 @@ Content-Type: application/json
|
||||
"type": "text/html"
|
||||
}
|
||||
],
|
||||
"timeStamp": "2019-09-08T18:57:10Z"
|
||||
"timeStamp": "2019-09-10T18:18:16Z"
|
||||
}
|
@ -22,5 +22,5 @@ Content-Type: application/json
|
||||
"type": "text/html"
|
||||
}
|
||||
],
|
||||
"timeStamp": "2019-09-08T18:57:10Z"
|
||||
"timeStamp": "2019-09-10T18:18:16Z"
|
||||
}
|
@ -33,5 +33,5 @@ Content-Type: application/json
|
||||
"type": "application/openapi+json;version=3.0"
|
||||
}
|
||||
],
|
||||
"timeStamp": "2019-09-08T18:57:11Z"
|
||||
"timeStamp": "2019-09-10T18:18:17Z"
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user