opengis: use correct json format only when available (#41224)
This commit is contained in:
parent
8b62667df0
commit
d47ed7717c
|
@ -73,18 +73,21 @@ class OpenGIS(BaseResource):
|
|||
class Meta:
|
||||
verbose_name = _('OpenGIS')
|
||||
|
||||
def get_service_version(self, service_type, service_url, renew=False):
|
||||
def get_capabilities(self, service_type, service_url):
|
||||
if not service_url:
|
||||
raise APIError('no %s URL declared' % service_type)
|
||||
return self.requests.get(
|
||||
service_url,
|
||||
params={'request': 'GetCapabilities', 'service': service_type.upper()},
|
||||
)
|
||||
|
||||
def get_service_version(self, service_type, service_url, renew=False):
|
||||
cache_key = 'opengis-%s-%s-version' % (service_type, self.id)
|
||||
if not renew:
|
||||
service_version = cache.get(cache_key)
|
||||
if service_version:
|
||||
return service_version
|
||||
response = self.requests.get(
|
||||
service_url,
|
||||
params={'request': 'GetCapabilities', 'service': service_type.upper()},
|
||||
)
|
||||
response = self.get_capabilities(service_type, service_url)
|
||||
element = ET.fromstring(response.content)
|
||||
service_version = element.attrib.get('version')
|
||||
# cache version number for an hour
|
||||
|
@ -97,6 +100,23 @@ class OpenGIS(BaseResource):
|
|||
def get_wfs_service_version(self, renew=False):
|
||||
return self.get_service_version('wfs', self.wfs_service_url, renew=renew)
|
||||
|
||||
def get_output_format(self):
|
||||
cache_key = 'opengis-%s-output-format' % self.id
|
||||
output_format = cache.get(cache_key)
|
||||
if output_format:
|
||||
return output_format
|
||||
response = self.get_capabilities('wfs', self.wfs_service_url)
|
||||
element = ET.fromstring(response.content)
|
||||
ns = {'ows': 'http://www.opengis.net/ows/1.1'}
|
||||
formats = element.findall('.//ows:Operation[@name="GetFeature"]/'
|
||||
'ows:Parameter[@name="outputFormat"]/'
|
||||
'ows:AllowedValues/ows:Value', ns)
|
||||
for output_format in formats:
|
||||
if 'json' in output_format.text.lower():
|
||||
cache.set(cache_key, output_format.text, 3600)
|
||||
return output_format.text
|
||||
raise APIError('WFS server doesn\'t support json output format for GetFeature request')
|
||||
|
||||
def get_typename_label(self):
|
||||
version_str = self.get_wfs_service_version()
|
||||
version_tuple = tuple(int(x) for x in version_str.split('.'))
|
||||
|
@ -123,6 +143,19 @@ class OpenGIS(BaseResource):
|
|||
})
|
||||
response.raise_for_status()
|
||||
|
||||
def build_get_features_params(self, typename=None, property_name=None, cql_filter=None):
|
||||
params = {
|
||||
'version': self.get_wfs_service_version(),
|
||||
'service': 'WFS',
|
||||
'request': 'GetFeature',
|
||||
self.get_typename_label(): typename,
|
||||
'propertyName': property_name,
|
||||
'outputFormat': self.get_output_format(),
|
||||
}
|
||||
if cql_filter:
|
||||
params['cql_filter'] = cql_filter
|
||||
return params
|
||||
|
||||
@endpoint(perm='can_access', description='Get features',
|
||||
parameters={
|
||||
'type_names': {
|
||||
|
@ -152,22 +185,14 @@ class OpenGIS(BaseResource):
|
|||
})
|
||||
def features(self, request, type_names, property_name, cql_filter=None,
|
||||
filter_property_name=None, q=None, **kwargs):
|
||||
params = {
|
||||
'VERSION': self.get_wfs_service_version(),
|
||||
'SERVICE': 'WFS',
|
||||
'REQUEST': 'GetFeature',
|
||||
self.get_typename_label(): type_names,
|
||||
'PROPERTYNAME': property_name,
|
||||
'OUTPUTFORMAT': 'json',
|
||||
}
|
||||
if cql_filter:
|
||||
params.update({'CQL_FILTER': cql_filter})
|
||||
if filter_property_name and q:
|
||||
if 'case-insensitive' in kwargs:
|
||||
operator = 'ILIKE'
|
||||
else:
|
||||
operator = 'LIKE'
|
||||
params['CQL_FILTER'] += ' AND %s %s \'%%%s%%\'' % (filter_property_name, operator, q)
|
||||
cql_filter += ' AND %s %s \'%%%s%%\'' % (filter_property_name, operator, q)
|
||||
params = self.build_get_features_params(type_names, property_name, cql_filter)
|
||||
response = self.requests.get(self.wfs_service_url, params=params)
|
||||
data = []
|
||||
try:
|
||||
|
@ -297,14 +322,7 @@ class OpenGIS(BaseResource):
|
|||
lon, lat = self.convert_coordinates(lon, lat)
|
||||
|
||||
cql_filter = 'DWITHIN(the_geom,Point(%.6f %.6f),%s,meters)' % (lon, lat, self.search_radius)
|
||||
params = {
|
||||
'VERSION': self.get_wfs_service_version(),
|
||||
'SERVICE': 'WFS',
|
||||
'REQUEST': 'GetFeature',
|
||||
self.get_typename_label(): self.query_layer,
|
||||
'OUTPUTFORMAT': 'json',
|
||||
'CQL_FILTER': cql_filter
|
||||
}
|
||||
params = self.build_get_features_params(typename=self.query_layer, cql_filter=cql_filter)
|
||||
response = self.requests.get(self.wfs_service_url, params=params)
|
||||
if not response.ok:
|
||||
raise APIError('Webservice returned status code %s' % response.status_code)
|
||||
|
|
|
@ -39,6 +39,16 @@ FAKE_SERVICE_CAPABILITIES = '''<?xml version="1.0" encoding="UTF-8"?>
|
|||
<ows:ServiceType>WFS</ows:ServiceType><ows:ServiceTypeVersion>2.0.0</ows:ServiceTypeVersion><ows:Fees/>
|
||||
<ows:AccessConstraints/>
|
||||
</ows:ServiceIdentification>
|
||||
<ows:OperationsMetadata>
|
||||
<ows:Operation name="GetFeature">
|
||||
<ows:Parameter name="outputFormat">
|
||||
<ows:AllowedValues>
|
||||
<ows:Value>application/gml+xml; version=3.2</ows:Value>
|
||||
<ows:Value>application/json; subtype=geojson</ows:Value>
|
||||
</ows:AllowedValues>
|
||||
</ows:Parameter>
|
||||
</ows:Operation>
|
||||
</ows:OperationsMetadata>
|
||||
</wfs:WFS_Capabilities>'''
|
||||
|
||||
FAKE_SERVICE_CAPABILITIES_V1_0_0 = '''<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
@ -50,6 +60,16 @@ FAKE_SERVICE_CAPABILITIES_V1_0_0 = '''<?xml version="1.0" encoding="UTF-8"?>
|
|||
<ows:ServiceType>WFS</ows:ServiceType><ows:ServiceTypeVersion>1.0.0</ows:ServiceTypeVersion><ows:Fees/>
|
||||
<ows:AccessConstraints/>
|
||||
</ows:ServiceIdentification>
|
||||
<ows:OperationsMetadata>
|
||||
<ows:Operation name="GetFeature">
|
||||
<ows:Parameter name="outputFormat">
|
||||
<ows:AllowedValues>
|
||||
<ows:Value>application/gml+xml; version=3.2</ows:Value>
|
||||
<ows:Value>application/json; subtype=geojson</ows:Value>
|
||||
</ows:AllowedValues>
|
||||
</ows:Parameter>
|
||||
</ows:Operation>
|
||||
</ows:OperationsMetadata>
|
||||
</wfs:WFS_Capabilities>'''
|
||||
|
||||
FAKE_FEATURES_JSON = '''
|
||||
|
@ -326,12 +346,12 @@ def test_get_feature(mocked_get, app, connector):
|
|||
assert endpoint == '/opengis/test/features'
|
||||
mocked_get.side_effect = geoserver_responses
|
||||
resp = app.get(endpoint, params={'type_names': 'ref_metro_limites_communales', 'property_name': 'nom'})
|
||||
assert mocked_get.call_args[1]['params']['REQUEST'] == 'GetFeature'
|
||||
assert mocked_get.call_args[1]['params']['PROPERTYNAME'] == 'nom'
|
||||
assert mocked_get.call_args[1]['params']['request'] == 'GetFeature'
|
||||
assert mocked_get.call_args[1]['params']['propertyName'] == 'nom'
|
||||
assert mocked_get.call_args[1]['params']['TYPENAMES'] == 'ref_metro_limites_communales'
|
||||
assert mocked_get.call_args[1]['params']['OUTPUTFORMAT'] == 'json'
|
||||
assert mocked_get.call_args[1]['params']['SERVICE'] == 'WFS'
|
||||
assert mocked_get.call_args[1]['params']['VERSION'] == connector.get_wfs_service_version()
|
||||
assert 'json' in mocked_get.call_args[1]['params']['outputFormat']
|
||||
assert mocked_get.call_args[1]['params']['service'] == 'WFS'
|
||||
assert mocked_get.call_args[1]['params']['version'] == connector.get_wfs_service_version()
|
||||
assert len(resp.json['data']) == 7
|
||||
for item in resp.json['data']:
|
||||
assert 'text' in item
|
||||
|
@ -347,7 +367,7 @@ def test_get_filtered_feature(mocked_get, app, connector):
|
|||
'property_name': 'nom',
|
||||
'cql_filter': 'nom=\'Fontaine\''
|
||||
})
|
||||
assert mocked_get.call_args[1]['params']['CQL_FILTER'] == 'nom=\'Fontaine\''
|
||||
assert mocked_get.call_args[1]['params']['cql_filter'] == 'nom=\'Fontaine\''
|
||||
|
||||
|
||||
@mock.patch('passerelle.utils.Request.get')
|
||||
|
@ -358,16 +378,16 @@ def test_get_filtered_by_property_feature(mocked_get, app, connector):
|
|||
'property_name': 'nom', 'cql_filter': 'nom=\'Fontaine\'',
|
||||
'filter_property_name': 'nom'}
|
||||
app.get(endpoint, params=params)
|
||||
assert mocked_get.call_args[1]['params']['CQL_FILTER'] == 'nom=\'Fontaine\''
|
||||
assert mocked_get.call_args[1]['params']['cql_filter'] == 'nom=\'Fontaine\''
|
||||
params['q'] = 'bens'
|
||||
app.get(endpoint, params=params)
|
||||
assert mocked_get.call_args[1]['params']['CQL_FILTER'] == 'nom=\'Fontaine\' AND nom LIKE \'%bens%\''
|
||||
assert mocked_get.call_args[1]['params']['cql_filter'] == 'nom=\'Fontaine\' AND nom LIKE \'%bens%\''
|
||||
params['case-insensitive'] = True
|
||||
app.get(endpoint, params=params)
|
||||
assert mocked_get.call_args[1]['params']['CQL_FILTER'] == 'nom=\'Fontaine\' AND nom ILIKE \'%bens%\''
|
||||
assert mocked_get.call_args[1]['params']['cql_filter'] == 'nom=\'Fontaine\' AND nom ILIKE \'%bens%\''
|
||||
params.pop('cql_filter')
|
||||
app.get(endpoint, params=params)
|
||||
assert 'CQL_FILTER' not in mocked_get.call_args[1]['params']
|
||||
assert 'cql_filter' not in mocked_get.call_args[1]['params']
|
||||
|
||||
|
||||
@mock.patch('passerelle.utils.Request.get')
|
||||
|
@ -379,12 +399,12 @@ def test_get_feature_error(mocked_get, app, connector):
|
|||
'type_names': 'ref_metro_limites_communales',
|
||||
'property_name': 'nom'
|
||||
})
|
||||
assert mocked_get.call_args[1]['params']['REQUEST'] == 'GetFeature'
|
||||
assert mocked_get.call_args[1]['params']['PROPERTYNAME'] == 'nom'
|
||||
assert mocked_get.call_args[1]['params']['request'] == 'GetFeature'
|
||||
assert mocked_get.call_args[1]['params']['propertyName'] == 'nom'
|
||||
assert mocked_get.call_args[1]['params']['TYPENAMES'] == 'ref_metro_limites_communales'
|
||||
assert mocked_get.call_args[1]['params']['OUTPUTFORMAT'] == 'json'
|
||||
assert mocked_get.call_args[1]['params']['SERVICE'] == 'WFS'
|
||||
assert mocked_get.call_args[1]['params']['VERSION'] == connector.get_wfs_service_version()
|
||||
assert 'json' in mocked_get.call_args[1]['params']['outputFormat']
|
||||
assert mocked_get.call_args[1]['params']['service'] == 'WFS'
|
||||
assert mocked_get.call_args[1]['params']['version'] == connector.get_wfs_service_version()
|
||||
result = resp.json
|
||||
assert result['err'] == 1
|
||||
assert result['err_desc'] == 'OpenGIS Error: NoApplicableCode'
|
||||
|
@ -400,12 +420,12 @@ def test_get_feature_error2(mocked_get, app, connector):
|
|||
'type_names': 'ref_metro_limites_communales',
|
||||
'property_name': 'nom'
|
||||
})
|
||||
assert mocked_get.call_args[1]['params']['REQUEST'] == 'GetFeature'
|
||||
assert mocked_get.call_args[1]['params']['PROPERTYNAME'] == 'nom'
|
||||
assert mocked_get.call_args[1]['params']['request'] == 'GetFeature'
|
||||
assert mocked_get.call_args[1]['params']['propertyName'] == 'nom'
|
||||
assert mocked_get.call_args[1]['params']['TYPENAMES'] == 'ref_metro_limites_communales'
|
||||
assert mocked_get.call_args[1]['params']['OUTPUTFORMAT'] == 'json'
|
||||
assert mocked_get.call_args[1]['params']['SERVICE'] == 'WFS'
|
||||
assert mocked_get.call_args[1]['params']['VERSION'] == connector.get_wfs_service_version()
|
||||
assert 'json' in mocked_get.call_args[1]['params']['outputFormat']
|
||||
assert mocked_get.call_args[1]['params']['service'] == 'WFS'
|
||||
assert mocked_get.call_args[1]['params']['version'] == connector.get_wfs_service_version()
|
||||
result = resp.json
|
||||
assert result['err'] == 1
|
||||
assert result['err_desc'] == 'OpenGIS Error: unparsable error'
|
||||
|
@ -421,8 +441,8 @@ def test_typename_parameter_upgrade(mocked_get, server_responses, version, typen
|
|||
assert endpoint == '/opengis/test/features'
|
||||
mocked_get.side_effect = server_responses
|
||||
resp = app.get(endpoint, params={'type_names': '...', 'property_name': '...'})
|
||||
assert mocked_get.call_args[1]['params']['REQUEST'] == 'GetFeature'
|
||||
assert mocked_get.call_args[1]['params']['VERSION'] == version
|
||||
assert mocked_get.call_args[1]['params']['request'] == 'GetFeature'
|
||||
assert mocked_get.call_args[1]['params']['version'] == version
|
||||
assert typename_label in mocked_get.call_args[1]['params'].keys()
|
||||
|
||||
|
||||
|
@ -445,7 +465,7 @@ def test_reverse_geocoding(mocked_get, app, connector):
|
|||
'lat': '45.1893469606986',
|
||||
'lon': '5.72462060798'
|
||||
})
|
||||
assert (mocked_get.call_args[1]['params']['CQL_FILTER']
|
||||
assert (mocked_get.call_args[1]['params']['cql_filter']
|
||||
== 'DWITHIN(the_geom,Point(1914061.486036 4224640.457791),45,meters)')
|
||||
assert resp.json['lon'] == '5.724077'
|
||||
assert resp.json['lat'] == '45.189397'
|
||||
|
@ -464,7 +484,7 @@ def test_reverse_geocoding(mocked_get, app, connector):
|
|||
'lat': '45.183784',
|
||||
'lon': '5.714885'
|
||||
})
|
||||
assert mocked_get.call_args[1]['params']['CQL_FILTER'] == 'DWITHIN(the_geom,Point(5.714885 45.183784),10,meters)'
|
||||
assert mocked_get.call_args[1]['params']['cql_filter'] == 'DWITHIN(the_geom,Point(5.714885 45.183784),10,meters)'
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_desc'] == 'Unable to geocode'
|
||||
|
||||
|
|
Loading…
Reference in New Issue