Skip to content

Commit 65b1f5f

Browse files
committed
add serializated arguments and parameters to request object.
1 parent 03fc480 commit 65b1f5f

File tree

4 files changed

+154
-2
lines changed

4 files changed

+154
-2
lines changed

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@ license = {file = "LICENSE"}
2323
name = "Flask-First"
2424
readme = "README.md"
2525
requires-python = ">=3.9"
26-
version = "0.17.0"
26+
version = "0.18.0"
2727

2828
[project.optional-dependencies]
2929
dev = [
3030
"bandit==1.7.7",
3131
"build==1.1.1",
3232
"mypy==1.8.0",
3333
"pre-commit==3.6.2",
34-
"pytest==8.0.2",
34+
"pytest==8.1.2",
3535
"pytest-cov==4.1.0",
3636
"python-dotenv==1.0.1",
3737
"tox==4.13.0",

tests/conftest.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import os
22
from collections.abc import Iterable
3+
from copy import deepcopy
34
from pathlib import Path
45

56
import pytest
7+
import yaml
68
from flask import Flask
79
from flask.testing import FlaskClient
810
from flask_first import First
@@ -59,3 +61,90 @@ def _create_app(path_to_spec: str, routes_functions: Iterable):
5961
return test_client
6062

6163
return _create_app
64+
65+
66+
@pytest.fixture
67+
def fx_make_minimal_spec() -> dict:
68+
return {
69+
'openapi': '3.1.0',
70+
'info': {'title': 'API for testing Flask-First', 'version': '1.0.0'},
71+
'paths': {
72+
'/endpoint': {
73+
'get': {
74+
'operationId': 'get_endpoint',
75+
'responses': {
76+
'200': {
77+
'description': 'OK',
78+
'content': {
79+
'application/json': {
80+
'schema': {
81+
'type': 'object',
82+
'properties': {'message': {'type': 'string'}},
83+
}
84+
}
85+
},
86+
}
87+
},
88+
},
89+
'post': {
90+
'operationId': 'post_endpoint',
91+
'requestBody': {
92+
'content': {
93+
'application/json': {
94+
'schema': {
95+
'type': 'object',
96+
'properties': {'message': {'type': 'string'}},
97+
}
98+
}
99+
}
100+
},
101+
'responses': {
102+
'201': {
103+
'description': 'OK',
104+
'content': {
105+
'application/json': {
106+
'schema': {
107+
'type': 'object',
108+
'properties': {'message': {'type': 'string'}},
109+
}
110+
}
111+
},
112+
},
113+
'default': {
114+
'description': 'OK',
115+
'content': {
116+
'application/json': {
117+
'schema': {
118+
'type': 'object',
119+
'properties': {'message': {'type': 'string'}},
120+
}
121+
}
122+
},
123+
},
124+
},
125+
},
126+
}
127+
},
128+
}
129+
130+
131+
@pytest.fixture
132+
def fx_make_spec_file(tmp_path, fx_make_minimal_spec):
133+
def _make_spec(paths: dict = None, url: str = None, parameters: list = None) -> Path:
134+
spec = deepcopy(fx_make_minimal_spec)
135+
136+
if paths:
137+
spec['paths'] = paths
138+
if parameters:
139+
spec['paths']['/endpoint']['parameters'] = parameters
140+
if url:
141+
spec['paths'][url] = deepcopy(spec['paths']['/endpoint'])
142+
spec['paths'].pop('/endpoint')
143+
144+
spec_file_path = tmp_path / 'openapi.yaml'
145+
with open(spec_file_path, 'w') as file:
146+
yaml.dump(spec, file)
147+
148+
return spec_file_path
149+
150+
return _make_spec

tests/test_spec_validator.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,20 @@ def test_spec_not_valid(spec):
4343

4444
with pytest.raises(FirstOpenAPIValidation):
4545
First(spec, app=app, swagger_ui_path='/docs')
46+
47+
48+
def test_spec_validator(fx_make_spec_file):
49+
spec = fx_make_spec_file()
50+
app = Flask('check_v30_specs')
51+
app.config['FIRST_RESPONSE_VALIDATION'] = True
52+
app.config['FIRST_EXPERIMENTAL_VALIDATOR'] = True
53+
first = First(spec, app=app, swagger_ui_path='/docs')
54+
55+
def get_endpoint() -> dict:
56+
return {'message': 'OK'}
57+
58+
first.add_view_func(get_endpoint)
59+
60+
r = app.test_client().get('/endpoint', follow_redirects=True)
61+
assert r.status_code == 200
62+
assert r.json['message'] == 'OK'

tests/test_tools.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from flask_first import request
2+
3+
4+
def test_tools__json(fx_make_spec_file, fx_create_app):
5+
spec_path = fx_make_spec_file()
6+
7+
def post_endpoint() -> tuple[dict, int]:
8+
json = request.extensions['first']['json']
9+
return json, 201
10+
11+
test_client = fx_create_app(spec_path, [post_endpoint])
12+
13+
r = test_client.post('/endpoint', json={'message': 'OK'})
14+
assert r.status_code == 201
15+
assert r.json == {'message': 'OK'}
16+
17+
18+
def test_tools__args(fx_make_spec_file, fx_create_app):
19+
parameters = [{'name': 'message', 'in': 'query', 'schema': {'type': 'string'}}]
20+
spec_path = fx_make_spec_file(parameters=parameters)
21+
22+
def get_endpoint() -> tuple[dict, int]:
23+
args = request.extensions['first']['args']
24+
return args
25+
26+
test_client = fx_create_app(spec_path, [get_endpoint])
27+
28+
r = test_client.get('/endpoint', query_string={'message': 'OK'})
29+
assert r.status_code == 200
30+
assert r.json == {'message': 'OK'}
31+
32+
33+
def test_tools__view_args(fx_make_spec_file, fx_create_app):
34+
url = '/endpoint/{message}'
35+
parameters = [{'name': 'message', 'in': 'path', 'required': True, 'schema': {'type': 'string'}}]
36+
spec_path = fx_make_spec_file(url=url, parameters=parameters)
37+
38+
def get_endpoint(message) -> tuple[dict, int]:
39+
view_args = request.extensions['first']['view_args']
40+
return view_args
41+
42+
test_client = fx_create_app(spec_path, [get_endpoint])
43+
44+
r = test_client.get('/endpoint/OK')
45+
assert r.status_code == 200
46+
assert r.json == {'message': 'OK'}

0 commit comments

Comments
 (0)