0001-utils-try-to-guess-type-of-params-38328.patch
passerelle/utils/api.py | ||
---|---|---|
148 | 148 |
return {} |
149 | 149 | |
150 | 150 |
def get_params(self): |
151 | ||
152 |
def type_to_str(value): |
|
153 |
if isinstance(value, bool): |
|
154 |
return 'boolean' |
|
155 |
elif isinstance(value, int): |
|
156 |
return 'integer' |
|
157 |
elif isinstance(value, float): |
|
158 |
return 'float' |
|
159 | ||
151 | 160 |
params = [] |
152 | 161 |
defaults = dict(zip( |
153 | 162 |
reversed(inspect.getargspec(self.func).args), |
... | ... | |
156 | 165 |
if param == 'post_data': |
157 | 166 |
continue |
158 | 167 |
param_info = {'name': param} |
159 |
if self.parameters and param in self.parameters and self.parameters[param].get('description'): |
|
160 |
param_info['description'] = self.parameters[param].get('description') |
|
168 |
if self.parameters and param in self.parameters: |
|
169 |
info = self.parameters[param] |
|
170 |
if info.get('description'): |
|
171 |
param_info['description'] = info['description'] |
|
172 |
if 'type' in info: |
|
173 |
typ = info['type'] |
|
174 |
if typ == 'int': |
|
175 |
param_info['type'] = 'integer' |
|
176 |
elif typ == 'bool': |
|
177 |
param_info['type'] = 'boolean' |
|
178 |
else: |
|
179 |
param_info['type'] = typ |
|
180 |
elif 'example_value' in info: |
|
181 |
typ = type_to_str(info['example_value']) |
|
182 |
if typ: |
|
183 |
param_info['type'] = typ |
|
161 | 184 |
if param in defaults: |
162 | 185 |
param_info['optional'] = True |
163 | 186 |
param_info['default_value'] = defaults[param] |
187 |
if 'type' not in param_info: |
|
188 |
typ = type_to_str(defaults[param]) |
|
189 |
if typ: |
|
190 |
param_info['type'] = typ |
|
164 | 191 |
params.append(param_info) |
165 | 192 |
return params |
passerelle/views.py | ||
---|---|---|
319 | 319 |
continue |
320 | 320 |
if not d.get(key): |
321 | 321 |
d[key] = other_params[key] |
322 |
if self.endpoint.endpoint_info.parameters:
|
|
322 |
for parameter_info in self.endpoint.endpoint_info.get_params():
|
|
323 | 323 |
# check and convert parameter values |
324 |
for parameter, parameter_info in self.endpoint.endpoint_info.parameters.items():
|
|
325 |
if parameter not in d:
|
|
326 |
continue
|
|
327 |
if parameter_info.get('type') == 'bool':
|
|
328 |
if d[parameter].lower() in ('true', 'on'):
|
|
329 |
d[parameter] = True
|
|
330 |
elif d[parameter].lower() in ('false', 'off'):
|
|
331 |
d[parameter] = False
|
|
332 |
else:
|
|
333 |
raise InvalidParameterValue(parameter)
|
|
334 |
elif parameter_info.get('type') == 'int':
|
|
335 |
try:
|
|
336 |
d[parameter] = int(d[parameter])
|
|
337 |
except ValueError:
|
|
338 |
raise InvalidParameterValue(parameter)
|
|
339 |
elif parameter_info.get('type') == 'float':
|
|
340 |
d[parameter] = d[parameter].replace(',', '.')
|
|
341 |
try:
|
|
342 |
d[parameter] = float(d[parameter])
|
|
343 |
except ValueError:
|
|
344 |
raise InvalidParameterValue(parameter)
|
|
324 |
parameter = parameter_info['name']
|
|
325 |
if parameter not in d: |
|
326 |
continue |
|
327 |
if parameter_info.get('type') in ('bool', 'boolean'):
|
|
328 |
if d[parameter].lower() in ('true', 'on'): |
|
329 |
d[parameter] = True |
|
330 |
elif d[parameter].lower() in ('false', 'off'): |
|
331 |
d[parameter] = False |
|
332 |
else: |
|
333 |
raise InvalidParameterValue(parameter) |
|
334 |
elif parameter_info.get('type') in ('int', 'integer'):
|
|
335 |
try: |
|
336 |
d[parameter] = int(d[parameter]) |
|
337 |
except ValueError: |
|
338 |
raise InvalidParameterValue(parameter) |
|
339 |
elif parameter_info.get('type') == 'float': |
|
340 |
d[parameter] = d[parameter].replace(',', '.') |
|
341 |
try: |
|
342 |
d[parameter] = float(d[parameter]) |
|
343 |
except ValueError: |
|
344 |
raise InvalidParameterValue(parameter) |
|
345 | 345 | |
346 | 346 |
if request.method == 'POST' and self.endpoint.endpoint_info.post: |
347 | 347 |
request_body = self.endpoint.endpoint_info.post.get('request_body', {}) |
tests/test_generic_endpoint.py | ||
---|---|---|
563 | 563 |
assert json_res['err_desc'] == 'invalid value for parameter "floating"' |
564 | 564 | |
565 | 565 | |
566 |
def test_endpoint_params_type_detection(app, db, monkeypatch): |
|
567 | ||
568 |
@endpoint(methods=['get'], |
|
569 |
parameters={ |
|
570 |
'bool_by_example': { |
|
571 |
'example_value': True, |
|
572 |
}, |
|
573 |
'int_by_example': { |
|
574 |
'example_value': 1, |
|
575 |
}, |
|
576 |
'float_by_example': { |
|
577 |
'example_value': 1.1, |
|
578 |
}, |
|
579 |
}) |
|
580 |
def httpcall(obj, request, boolean=False, integer=1, floating=1.1, |
|
581 |
bool_by_example=None, int_by_example=None, float_by_example=None): |
|
582 |
return {'boolean': boolean, 'integer': integer, 'floating': floating, |
|
583 |
'bool_by_example': bool_by_example, 'int_by_example': int_by_example, |
|
584 |
'float_by_example': float_by_example} |
|
585 | ||
586 |
monkeypatch.setattr(StubInvoicesConnector, 'httpcall', httpcall, raising=False) |
|
587 | ||
588 |
connector = StubInvoicesConnector(slug='fake') |
|
589 |
connector.save() |
|
590 | ||
591 |
json_res = app.get('/stub-invoices/fake/httpcall?boolean=True').json |
|
592 |
assert json_res['boolean'] is True |
|
593 |
json_res = app.get('/stub-invoices/fake/httpcall?bool_by_example=True').json |
|
594 |
assert json_res['bool_by_example'] is True |
|
595 | ||
596 |
json_res = app.get('/stub-invoices/fake/httpcall?integer=2').json |
|
597 |
assert json_res['integer'] == 2 |
|
598 |
json_res = app.get('/stub-invoices/fake/httpcall?int_by_example=2').json |
|
599 |
assert json_res['int_by_example'] == 2 |
|
600 | ||
601 |
json_res = app.get('/stub-invoices/fake/httpcall?floating=1.5').json |
|
602 |
assert json_res['floating'] == 1.5 |
|
603 |
json_res = app.get('/stub-invoices/fake/httpcall?float_by_example=1.5').json |
|
604 |
assert json_res['float_by_example'] == 1.5 |
|
605 | ||
606 |
res = app.get('/stub-invoices/fake/') |
|
607 |
for param in res.pyquery('ul.get-params li'): |
|
608 |
param_details = param.getchildren() |
|
609 |
name = next(el for el in param_details if 'param-name' in el.attrib['class']).text |
|
610 |
typ = next(el for el in param_details if 'type' in el.attrib['class']).text |
|
611 |
typ = typ.strip('()') |
|
612 |
if 'bool' in name: |
|
613 |
assert typ == 'boolean' |
|
614 |
elif 'int' in name: |
|
615 |
assert typ == 'integer' |
|
616 |
elif 'float' in name: |
|
617 |
assert typ == 'float' |
|
618 |
else: |
|
619 |
assert typ == 'string' |
|
620 | ||
621 | ||
566 | 622 |
class DummyConnectorBase(BaseResource): |
567 | 623 |
def get_availability_status(self): |
568 | 624 |
# naive get_availability_status method for testing |
569 |
- |