Projet

Général

Profil

0001-opengis-use-correct-json-format-only-when-available-.patch

Valentin Deniaud, 01 avril 2020 16:19

Télécharger (13,9 ko)

Voir les différences:

Subject: [PATCH 1/2] opengis: use correct json format only when available
 (#41224)

 passerelle/apps/opengis/models.py | 64 ++++++++++++++++++-----------
 tests/test_opengis.py             | 68 ++++++++++++++++++++-----------
 2 files changed, 85 insertions(+), 47 deletions(-)
passerelle/apps/opengis/models.py
73 73
    class Meta:
74 74
        verbose_name = _('OpenGIS')
75 75

  
76
    def get_service_version(self, service_type, service_url, renew=False):
76
    def get_capabilities(self, service_type, service_url):
77 77
        if not service_url:
78 78
            raise APIError('no %s URL declared' % service_type)
79
        return self.requests.get(
80
            service_url,
81
            params={'request': 'GetCapabilities', 'service': service_type.upper()},
82
        )
83

  
84
    def get_service_version(self, service_type, service_url, renew=False):
79 85
        cache_key = 'opengis-%s-%s-version' % (service_type, self.id)
80 86
        if not renew:
81 87
            service_version = cache.get(cache_key)
82 88
            if service_version:
83 89
                return service_version
84
        response = self.requests.get(
85
                service_url,
86
                params={'request': 'GetCapabilities', 'service': service_type.upper()},
87
                )
90
        response = self.get_capabilities(service_type, service_url)
88 91
        element = ET.fromstring(response.content)
89 92
        service_version = element.attrib.get('version')
90 93
        # cache version number for an hour
......
97 100
    def get_wfs_service_version(self, renew=False):
98 101
        return self.get_service_version('wfs', self.wfs_service_url, renew=renew)
99 102

  
103
    def get_output_format(self):
104
        cache_key = 'opengis-%s-output-format' % (self.id)
105
        output_format = cache.get(cache_key)
106
        if output_format:
107
            return output_format
108
        response = self.get_capabilities('wfs', self.wfs_service_url)
109
        element = ET.fromstring(response.content)
110
        ns = {'ows': 'http://www.opengis.net/ows/1.1'}
111
        formats = element.findall('.//ows:Operation[@name="GetFeature"]/'
112
                                  'ows:Parameter[@name="outputFormat"]/'
113
                                  'ows:AllowedValues/ows:Value', ns)
114
        for output_format in formats:
115
            if 'json' in output_format.text.lower():
116
                cache.set(cache_key, output_format.text)
117
                return output_format.text
118
        raise APIError('WFS server doesn\'t support json output format for GetFeature request')
119

  
100 120
    def get_typename_label(self):
101 121
        version_str = self.get_wfs_service_version()
102 122
        version_tuple = tuple(int(x) for x in version_str.split('.'))
......
123 143
                })
124 144
            response.raise_for_status()
125 145

  
146
    def build_get_features_params(self, typename=None, property_name=None, cql_filter=None):
147
        params = {
148
            'version': self.get_wfs_service_version(),
149
            'service': 'WFS',
150
            'request': 'GetFeature',
151
            self.get_typename_label(): typename,
152
            'propertyName': property_name,
153
            'outputFormat': self.get_output_format(),
154
        }
155
        if cql_filter:
156
            params['cql_filter'] = cql_filter
157
        return params
158

  
126 159
    @endpoint(perm='can_access', description='Get features',
127 160
              parameters={
128 161
                  'type_names': {
......
152 185
              })
153 186
    def features(self, request, type_names, property_name, cql_filter=None,
154 187
                 filter_property_name=None, q=None, **kwargs):
155
        params = {
156
            'VERSION': self.get_wfs_service_version(),
157
            'SERVICE': 'WFS',
158
            'REQUEST': 'GetFeature',
159
            self.get_typename_label(): type_names,
160
            'PROPERTYNAME': property_name,
161
            'OUTPUTFORMAT': 'json',
162
        }
163 188
        if cql_filter:
164
            params.update({'CQL_FILTER': cql_filter})
165 189
            if filter_property_name and q:
166 190
                if 'case-insensitive' in kwargs:
167 191
                    operator = 'ILIKE'
168 192
                else:
169 193
                    operator = 'LIKE'
170
                params['CQL_FILTER'] += ' AND %s %s \'%%%s%%\'' % (filter_property_name, operator, q)
194
                cql_filter += ' AND %s %s \'%%%s%%\'' % (filter_property_name, operator, q)
195
        params = self.build_get_features_params(type_names, property_name, cql_filter)
171 196
        response = self.requests.get(self.wfs_service_url, params=params)
172 197
        data = []
173 198
        try:
......
297 322
        lon, lat = self.convert_coordinates(lon, lat)
298 323

  
299 324
        cql_filter = 'DWITHIN(the_geom,Point(%.6f %.6f),%s,meters)' % (lon, lat, self.search_radius)
300
        params = {
301
            'VERSION': self.get_wfs_service_version(),
302
            'SERVICE': 'WFS',
303
            'REQUEST': 'GetFeature',
304
            self.get_typename_label(): self.query_layer,
305
            'OUTPUTFORMAT': 'json',
306
            'CQL_FILTER': cql_filter
307
        }
325
        params = self.build_get_features_params(typename=self.query_layer, cql_filter=cql_filter)
308 326
        response = self.requests.get(self.wfs_service_url, params=params)
309 327
        if not response.ok:
310 328
            raise APIError('Webservice returned status code %s' % response.status_code)
tests/test_opengis.py
39 39
        <ows:ServiceType>WFS</ows:ServiceType><ows:ServiceTypeVersion>2.0.0</ows:ServiceTypeVersion><ows:Fees/>
40 40
        <ows:AccessConstraints/>
41 41
    </ows:ServiceIdentification>
42
    <ows:OperationsMetadata>
43
        <ows:Operation name="GetFeature">
44
            <ows:Parameter name="outputFormat">
45
                <ows:AllowedValues>
46
                    <ows:Value>application/gml+xml; version=3.2</ows:Value>
47
                    <ows:Value>application/json; subtype=geojson</ows:Value>
48
                </ows:AllowedValues>
49
            </ows:Parameter>
50
        </ows:Operation>
51
    </ows:OperationsMetadata>
42 52
</wfs:WFS_Capabilities>'''
43 53

  
44 54
FAKE_SERVICE_CAPABILITIES_V1_0_0 = '''<?xml version="1.0" encoding="UTF-8"?>
......
50 60
        <ows:ServiceType>WFS</ows:ServiceType><ows:ServiceTypeVersion>1.0.0</ows:ServiceTypeVersion><ows:Fees/>
51 61
        <ows:AccessConstraints/>
52 62
    </ows:ServiceIdentification>
63
    <ows:OperationsMetadata>
64
        <ows:Operation name="GetFeature">
65
            <ows:Parameter name="outputFormat">
66
                <ows:AllowedValues>
67
                    <ows:Value>application/gml+xml; version=3.2</ows:Value>
68
                    <ows:Value>application/json; subtype=geojson</ows:Value>
69
                </ows:AllowedValues>
70
            </ows:Parameter>
71
        </ows:Operation>
72
    </ows:OperationsMetadata>
53 73
</wfs:WFS_Capabilities>'''
54 74

  
55 75
FAKE_FEATURES_JSON = '''
......
326 346
    assert endpoint == '/opengis/test/features'
327 347
    mocked_get.side_effect = geoserver_responses
328 348
    resp = app.get(endpoint, params={'type_names': 'ref_metro_limites_communales', 'property_name': 'nom'})
329
    assert mocked_get.call_args[1]['params']['REQUEST'] == 'GetFeature'
330
    assert mocked_get.call_args[1]['params']['PROPERTYNAME'] == 'nom'
349
    assert mocked_get.call_args[1]['params']['request'] == 'GetFeature'
350
    assert mocked_get.call_args[1]['params']['propertyName'] == 'nom'
331 351
    assert mocked_get.call_args[1]['params']['TYPENAMES'] == 'ref_metro_limites_communales'
332
    assert mocked_get.call_args[1]['params']['OUTPUTFORMAT'] == 'json'
333
    assert mocked_get.call_args[1]['params']['SERVICE'] == 'WFS'
334
    assert mocked_get.call_args[1]['params']['VERSION'] == connector.get_wfs_service_version()
352
    assert 'json' in mocked_get.call_args[1]['params']['outputFormat']
353
    assert mocked_get.call_args[1]['params']['service'] == 'WFS'
354
    assert mocked_get.call_args[1]['params']['version'] == connector.get_wfs_service_version()
335 355
    assert len(resp.json['data']) == 7
336 356
    for item in resp.json['data']:
337 357
        assert 'text' in item
......
347 367
                'property_name': 'nom',
348 368
                'cql_filter': 'nom=\'Fontaine\''
349 369
            })
350
    assert mocked_get.call_args[1]['params']['CQL_FILTER'] == 'nom=\'Fontaine\''
370
    assert mocked_get.call_args[1]['params']['cql_filter'] == 'nom=\'Fontaine\''
351 371

  
352 372

  
353 373
@mock.patch('passerelle.utils.Request.get')
......
358 378
              'property_name': 'nom', 'cql_filter': 'nom=\'Fontaine\'',
359 379
              'filter_property_name': 'nom'}
360 380
    app.get(endpoint, params=params)
361
    assert mocked_get.call_args[1]['params']['CQL_FILTER'] == 'nom=\'Fontaine\''
381
    assert mocked_get.call_args[1]['params']['cql_filter'] == 'nom=\'Fontaine\''
362 382
    params['q'] = 'bens'
363 383
    app.get(endpoint, params=params)
364
    assert mocked_get.call_args[1]['params']['CQL_FILTER'] == 'nom=\'Fontaine\' AND nom LIKE \'%bens%\''
384
    assert mocked_get.call_args[1]['params']['cql_filter'] == 'nom=\'Fontaine\' AND nom LIKE \'%bens%\''
365 385
    params['case-insensitive'] = True
366 386
    app.get(endpoint, params=params)
367
    assert mocked_get.call_args[1]['params']['CQL_FILTER'] == 'nom=\'Fontaine\' AND nom ILIKE \'%bens%\''
387
    assert mocked_get.call_args[1]['params']['cql_filter'] == 'nom=\'Fontaine\' AND nom ILIKE \'%bens%\''
368 388
    params.pop('cql_filter')
369 389
    app.get(endpoint, params=params)
370
    assert 'CQL_FILTER' not in mocked_get.call_args[1]['params']
390
    assert 'cql_filter' not in mocked_get.call_args[1]['params']
371 391

  
372 392

  
373 393
@mock.patch('passerelle.utils.Request.get')
......
379 399
        'type_names': 'ref_metro_limites_communales',
380 400
        'property_name': 'nom'
381 401
    })
382
    assert mocked_get.call_args[1]['params']['REQUEST'] == 'GetFeature'
383
    assert mocked_get.call_args[1]['params']['PROPERTYNAME'] == 'nom'
402
    assert mocked_get.call_args[1]['params']['request'] == 'GetFeature'
403
    assert mocked_get.call_args[1]['params']['propertyName'] == 'nom'
384 404
    assert mocked_get.call_args[1]['params']['TYPENAMES'] == 'ref_metro_limites_communales'
385
    assert mocked_get.call_args[1]['params']['OUTPUTFORMAT'] == 'json'
386
    assert mocked_get.call_args[1]['params']['SERVICE'] == 'WFS'
387
    assert mocked_get.call_args[1]['params']['VERSION'] == connector.get_wfs_service_version()
405
    assert 'json' in mocked_get.call_args[1]['params']['outputFormat']
406
    assert mocked_get.call_args[1]['params']['service'] == 'WFS'
407
    assert mocked_get.call_args[1]['params']['version'] == connector.get_wfs_service_version()
388 408
    result = resp.json
389 409
    assert result['err'] == 1
390 410
    assert result['err_desc'] == 'OpenGIS Error: NoApplicableCode'
......
400 420
        'type_names': 'ref_metro_limites_communales',
401 421
        'property_name': 'nom'
402 422
    })
403
    assert mocked_get.call_args[1]['params']['REQUEST'] == 'GetFeature'
404
    assert mocked_get.call_args[1]['params']['PROPERTYNAME'] == 'nom'
423
    assert mocked_get.call_args[1]['params']['request'] == 'GetFeature'
424
    assert mocked_get.call_args[1]['params']['propertyName'] == 'nom'
405 425
    assert mocked_get.call_args[1]['params']['TYPENAMES'] == 'ref_metro_limites_communales'
406
    assert mocked_get.call_args[1]['params']['OUTPUTFORMAT'] == 'json'
407
    assert mocked_get.call_args[1]['params']['SERVICE'] == 'WFS'
408
    assert mocked_get.call_args[1]['params']['VERSION'] == connector.get_wfs_service_version()
426
    assert 'json' in mocked_get.call_args[1]['params']['outputFormat']
427
    assert mocked_get.call_args[1]['params']['service'] == 'WFS'
428
    assert mocked_get.call_args[1]['params']['version'] == connector.get_wfs_service_version()
409 429
    result = resp.json
410 430
    assert result['err'] == 1
411 431
    assert result['err_desc'] == 'OpenGIS Error: unparsable error'
......
421 441
    assert endpoint == '/opengis/test/features'
422 442
    mocked_get.side_effect = server_responses
423 443
    resp = app.get(endpoint, params={'type_names': '...', 'property_name': '...'})
424
    assert mocked_get.call_args[1]['params']['REQUEST'] == 'GetFeature'
425
    assert mocked_get.call_args[1]['params']['VERSION'] == version
444
    assert mocked_get.call_args[1]['params']['request'] == 'GetFeature'
445
    assert mocked_get.call_args[1]['params']['version'] == version
426 446
    assert typename_label in mocked_get.call_args[1]['params'].keys()
427 447

  
428 448

  
......
445 465
                       'lat': '45.1893469606986',
446 466
                       'lon': '5.72462060798'
447 467
                   })
448
    assert (mocked_get.call_args[1]['params']['CQL_FILTER']
468
    assert (mocked_get.call_args[1]['params']['cql_filter']
449 469
            == 'DWITHIN(the_geom,Point(1914061.486036 4224640.457791),45,meters)')
450 470
    assert resp.json['lon'] == '5.724077'
451 471
    assert resp.json['lat'] == '45.189397'
......
464 484
            'lat': '45.183784',
465 485
            'lon': '5.714885'
466 486
        })
467
    assert mocked_get.call_args[1]['params']['CQL_FILTER'] == 'DWITHIN(the_geom,Point(5.714885 45.183784),10,meters)'
487
    assert mocked_get.call_args[1]['params']['cql_filter'] == 'DWITHIN(the_geom,Point(5.714885 45.183784),10,meters)'
468 488
    assert resp.json['err'] == 1
469 489
    assert resp.json['err_desc'] == 'Unable to geocode'
470 490

  
471
-