Projet

Général

Profil

0001-jsonschema-pre_process-the-payload-before-schema-val.patch

Lauréline Guérin, 14 janvier 2020 10:38

Télécharger (6,23 ko)

Voir les différences:

Subject: [PATCH] jsonschema: pre_process the payload, before schema validation
 (#38933)

 passerelle/views.py            |   3 +
 tests/test_generic_endpoint.py | 126 ++++++++++++++++++++++++++++++++-
 2 files changed, 128 insertions(+), 1 deletion(-)
passerelle/views.py
348 348
                json_schema = request_body['schema']['application/json']
349 349
                must_unflatten = hasattr(json_schema, 'items') and json_schema.get('unflatten', False)
350 350
                merge_extra = hasattr(json_schema, 'items') and json_schema.get('merge_extra', False)
351
                pre_process = hasattr(json_schema, 'items') and json_schema.get('pre_process')
351 352
                try:
352 353
                    data = json.loads(request.body)
353 354
                except ValueError as e:
......
356 357
                    data = unflatten(data)
357 358
                if merge_extra and hasattr(data, 'items'):
358 359
                    data.update(data.pop('extra', {}))
360
                if pre_process is not None:
361
                    pre_process(self.endpoint.im_self, data)
359 362
                try:
360 363
                    validate(data, json_schema)
361 364
                except ValidationError as e:
tests/test_generic_endpoint.py
17 17

  
18 18
from __future__ import unicode_literals
19 19

  
20
import copy
20 21
import os
21 22
import json
22 23
import random
......
27 28

  
28 29
import utils
29 30

  
31
from django.contrib.contenttypes.models import ContentType
30 32
from django.core.urlresolvers import reverse
31 33

  
32 34
from passerelle.apps.arcgis.models import ArcGIS
33
from passerelle.base.models import ResourceLog, ProxyLogger, BaseResource, HTTPResource
35
from passerelle.base.models import ResourceLog, ProxyLogger, BaseResource, HTTPResource, LoggingParameters
34 36
from passerelle.base.models import ResourceStatus
35 37
from passerelle.apps.mdel.models import MDEL
36 38
from passerelle.contrib.stub_invoices.models import StubInvoicesConnector
......
267 269
    connector.foo9.endpoint_info.http_method = 'post'
268 270
    assert connector.foo9.endpoint_info.long_description == 'foo9 post'
269 271

  
272

  
273
class FakeJSONConnector(object):
274
    slug = 'connector-json'
275
    log_level = 'DEBUG'
276

  
277
    FOO_SCHEMA = {
278
        'properties': {
279
            'foo': {
280
                'type': 'array',
281
                'items': {
282
                    'properties': {
283
                        'id': {'type': 'integer'},
284
                        'bar': {'type': 'boolean'}
285
                    },
286
                    'required': ['id', 'bar']
287
                }
288
            }
289
        }
290
    }
291
    BAR_SCHEMA = copy.deepcopy(FOO_SCHEMA)
292

  
293
    def __init__(self, *args, **kwargs):
294
        super(FakeJSONConnector, self).__init__(*args, **kwargs)
295
        self.logger = ProxyLogger(connector=self)
296

  
297
    @property
298
    def logging_parameters(self):
299
        return LoggingParameters()
300

  
301
    def get_connector_slug(self):
302
        return 'connector-json'
303

  
304
    def down(self):
305
        return False
306

  
307
    def pre_process(self, post_data):
308
        foo_objects = copy.deepcopy(post_data.get('foo', []))
309
        foo_objects.reverse()
310
        foo_len = len(foo_objects)
311
        for i, foo in enumerate(foo_objects):
312
            if not foo.get('id'):
313
                # id is empty, remove foo object
314
                post_data['foo'].pop(foo_len - i - 1)
315
        return post_data
316

  
317
    BAR_SCHEMA['pre_process'] = pre_process
318

  
319
    @endpoint(
320
        post={
321
            'request_body': {
322
                'schema': {
323
                    'application/json': FOO_SCHEMA
324
                }
325
            }
326
        })
327
    def foo(self, request, post_data):
328
        return {'data': post_data}
329

  
330
    @endpoint(
331
        post={
332
            'request_body': {
333
                'schema': {
334
                    'application/json': BAR_SCHEMA
335
                }
336
            }
337
        })
338
    def bar(self, request, post_data):
339
        return {'data': post_data}
340

  
341

  
342
def test_endpoint_decorator_pre_process(db, app):
343
    connector = FakeJSONConnector()
344

  
345
    patch_init = mock.patch('passerelle.views.GenericConnectorMixin.init_stuff')
346
    patch_object = mock.patch('passerelle.views.GenericEndpointView.get_object',
347
                              return_value=connector)
348

  
349
    url_foo = reverse('generic-endpoint', kwargs={
350
        'connector': 'connector-json',
351
        'slug': 'connector-json',
352
        'endpoint': 'foo',
353
    })
354
    url_bar = reverse('generic-endpoint', kwargs={
355
        'connector': 'connector-json',
356
        'slug': 'connector-json',
357
        'endpoint': 'bar',
358
    })
359

  
360
    payload = {
361
        'foo': [
362
            {'id': 42, 'bar': True}
363
        ]
364
    }
365
    with patch_init, patch_object:
366
        resp = app.post_json(url_foo, params=payload)
367
    assert resp.json['err'] == 0
368
    assert resp.json['data'] == payload
369
    with patch_init, patch_object:
370
        resp = app.post_json(url_bar, params=payload)
371
    assert resp.json['err'] == 0
372
    assert resp.json['data'] == payload
373

  
374
    payload = {
375
        'foo': [
376
            {'id': 42, 'bar': True},
377
            {'id': None, 'bar': False}  # invalid object
378
        ]
379
    }
380
    with patch_init, patch_object:
381
        resp = app.post_json(url_foo, params=payload, status=400)
382
    assert resp.json['err'] == 1
383
    assert resp.json['err_desc'] == "None is not of type u'integer'"
384
    with patch_init, patch_object:
385
        resp = app.post_json(url_bar, params=payload)
386
    assert resp.json['err'] == 0
387
    assert resp.json['data'] == {
388
        'foo': [
389
            {'id': 42, 'bar': True}
390
        ]
391
    }
392

  
393

  
270 394
def test_endpoint_description_in_template(app, db):
271 395
    StubInvoicesConnector(slug='fake').save()
272 396
    resp = app.get('/stub-invoices/fake/')
273
-