Schema-First 0.16.0__tar.gz → 0.16.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {schema_first-0.16.0 → schema_first-0.16.2}/PKG-INFO +3 -3
- {schema_first-0.16.0 → schema_first-0.16.2}/pyproject.toml +3 -3
- {schema_first-0.16.0 → schema_first-0.16.2}/src/Schema_First.egg-info/PKG-INFO +3 -3
- {schema_first-0.16.0 → schema_first-0.16.2}/src/Schema_First.egg-info/requires.txt +2 -2
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/query/validator.py +16 -3
- {schema_first-0.16.0 → schema_first-0.16.2}/tests/test_query_validators.py +60 -3
- {schema_first-0.16.0 → schema_first-0.16.2}/README.md +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/setup.cfg +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/Schema_First.egg-info/SOURCES.txt +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/Schema_First.egg-info/dependency_links.txt +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/Schema_First.egg-info/top_level.txt +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/__init__.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/exceptions.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/loaders/__init__.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/loaders/exc.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/loaders/yaml_loader.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/__init__.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/exc.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/__init__.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/base.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/constants.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/fields.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/v3_2/components_object_schema.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/v3_2/contact_object_schema.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/v3_2/example_object_schema.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/v3_2/external_docs_object_schema.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/v3_2/header_object_schema.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/v3_2/info_object_schema.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/v3_2/license_object_schema.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/v3_2/media_type_object_schema.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/v3_2/openapi_object_schema.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/v3_2/operation_object_schema.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/v3_2/parameter_object_schema.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/v3_2/path_item_object_schema.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/v3_2/reference_object_schema.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/v3_2/request_body_object_schema.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/v3_2/responses_object_schema.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/v3_2/schema_object_schema.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/v3_2/security_scheme_object_schema.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/v3_2/server_object_schema.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/v3_2/server_variable_object_schema.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/v3_2/tag_object_schema.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/openapi/schemas/validators.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/query/exceptions.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/specification/__init__.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/src/schema_first/specification/spilli_api.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/tests/test_specification.py +0 -0
- {schema_first-0.16.0 → schema_first-0.16.2}/tests/test_validator.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Schema-First
|
|
3
|
-
Version: 0.16.
|
|
3
|
+
Version: 0.16.2
|
|
4
4
|
Summary: OpenAPI specification validator and converter to Marshmallow schemas.
|
|
5
5
|
Author-email: Konstantin Fadeev <jredkiy@yandex.ru>
|
|
6
6
|
License-Expression: LGPL-2.1-only
|
|
@@ -16,11 +16,11 @@ Provides-Extra: dev
|
|
|
16
16
|
Requires-Dist: bandit>=1.9.3; extra == "dev"
|
|
17
17
|
Requires-Dist: build>=1.4.0; extra == "dev"
|
|
18
18
|
Requires-Dist: openapi-spec-validator>=0.7.1; extra == "dev"
|
|
19
|
-
Requires-Dist: pre-commit>=4.
|
|
19
|
+
Requires-Dist: pre-commit>=4.6.2; extra == "dev"
|
|
20
20
|
Requires-Dist: pytest>=9.1.1; extra == "dev"
|
|
21
21
|
Requires-Dist: pytest-cov>=7.0.0; extra == "dev"
|
|
22
22
|
Requires-Dist: python-dotenv>=1.2.1; extra == "dev"
|
|
23
|
-
Requires-Dist: tox==4.
|
|
23
|
+
Requires-Dist: tox==4.59.0; extra == "dev"
|
|
24
24
|
Requires-Dist: twine==6.2.0; extra == "dev"
|
|
25
25
|
|
|
26
26
|
# Schema-First
|
|
@@ -20,18 +20,18 @@ license-files = ["LICENSE.txt"]
|
|
|
20
20
|
name = "Schema-First"
|
|
21
21
|
readme = "README.md"
|
|
22
22
|
requires-python = ">=3.12"
|
|
23
|
-
version = "0.16.
|
|
23
|
+
version = "0.16.2"
|
|
24
24
|
|
|
25
25
|
[project.optional-dependencies]
|
|
26
26
|
dev = [
|
|
27
27
|
"bandit>=1.9.3",
|
|
28
28
|
"build>=1.4.0",
|
|
29
29
|
'openapi-spec-validator>=0.7.1',
|
|
30
|
-
"pre-commit>=4.
|
|
30
|
+
"pre-commit>=4.6.2",
|
|
31
31
|
"pytest>=9.1.1",
|
|
32
32
|
"pytest-cov>=7.0.0",
|
|
33
33
|
"python-dotenv>=1.2.1",
|
|
34
|
-
"tox==4.
|
|
34
|
+
"tox==4.59.0",
|
|
35
35
|
"twine==6.2.0"
|
|
36
36
|
]
|
|
37
37
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Schema-First
|
|
3
|
-
Version: 0.16.
|
|
3
|
+
Version: 0.16.2
|
|
4
4
|
Summary: OpenAPI specification validator and converter to Marshmallow schemas.
|
|
5
5
|
Author-email: Konstantin Fadeev <jredkiy@yandex.ru>
|
|
6
6
|
License-Expression: LGPL-2.1-only
|
|
@@ -16,11 +16,11 @@ Provides-Extra: dev
|
|
|
16
16
|
Requires-Dist: bandit>=1.9.3; extra == "dev"
|
|
17
17
|
Requires-Dist: build>=1.4.0; extra == "dev"
|
|
18
18
|
Requires-Dist: openapi-spec-validator>=0.7.1; extra == "dev"
|
|
19
|
-
Requires-Dist: pre-commit>=4.
|
|
19
|
+
Requires-Dist: pre-commit>=4.6.2; extra == "dev"
|
|
20
20
|
Requires-Dist: pytest>=9.1.1; extra == "dev"
|
|
21
21
|
Requires-Dist: pytest-cov>=7.0.0; extra == "dev"
|
|
22
22
|
Requires-Dist: python-dotenv>=1.2.1; extra == "dev"
|
|
23
|
-
Requires-Dist: tox==4.
|
|
23
|
+
Requires-Dist: tox==4.59.0; extra == "dev"
|
|
24
24
|
Requires-Dist: twine==6.2.0; extra == "dev"
|
|
25
25
|
|
|
26
26
|
# Schema-First
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import typing as t
|
|
2
2
|
|
|
3
3
|
from marshmallow import fields
|
|
4
|
+
from marshmallow import INCLUDE
|
|
4
5
|
from marshmallow import Schema
|
|
5
6
|
from marshmallow import ValidationError
|
|
6
7
|
|
|
@@ -49,10 +50,18 @@ class HTTPQueryValidator:
|
|
|
49
50
|
if not params_schema:
|
|
50
51
|
continue
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
if name_params == 'headers':
|
|
54
|
+
request_fields[name_params] = fields.Nested(params_schema, unknown=INCLUDE)
|
|
55
|
+
else:
|
|
56
|
+
request_fields[name_params] = fields.Nested(params_schema)
|
|
57
|
+
|
|
58
|
+
if 'headers' not in parameters:
|
|
59
|
+
request_fields['headers'] = fields.Raw()
|
|
60
|
+
|
|
61
|
+
if not parameters:
|
|
62
|
+
request_fields['headers'] = fields.Raw()
|
|
53
63
|
|
|
54
64
|
request_schema = Schema.from_dict(request_fields)
|
|
55
|
-
request_schema.Meta.unknown = 'raise'
|
|
56
65
|
|
|
57
66
|
return request_schema
|
|
58
67
|
|
|
@@ -76,6 +85,10 @@ class HTTPQueryValidator:
|
|
|
76
85
|
body_schema = content_type.get('schema')
|
|
77
86
|
if body_schema:
|
|
78
87
|
response_fields['body'] = fields.Nested(body_schema)
|
|
88
|
+
else:
|
|
89
|
+
# If body must be empty.
|
|
90
|
+
if data.get('content_type'):
|
|
91
|
+
response_fields['content_type'] = fields.String()
|
|
79
92
|
|
|
80
93
|
response_schema = Schema.from_dict(response_fields)
|
|
81
94
|
response_schema.Meta.unknown = 'raise'
|
|
@@ -119,7 +132,7 @@ class HTTPQueryValidator:
|
|
|
119
132
|
endpoint: str,
|
|
120
133
|
method: t.Literal['post', 'get', 'update', 'patch', 'delete'],
|
|
121
134
|
status_code: str,
|
|
122
|
-
content_type:
|
|
135
|
+
content_type: str or ... = ...,
|
|
123
136
|
body: dict[str, t.Any] or ... = ...,
|
|
124
137
|
):
|
|
125
138
|
all_kwargs = {
|
|
@@ -4,6 +4,7 @@ from schema_first.query.exceptions import EndpointValidation
|
|
|
4
4
|
from schema_first.query.exceptions import QueryValidatorException
|
|
5
5
|
from schema_first.query.exceptions import ResponseValidation
|
|
6
6
|
from schema_first.query.validator import HTTPQueryValidator
|
|
7
|
+
from schema_first.query.validator import RequestValidation
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
def test_query_validators__request(fx_openapi_3_2_0, fx_open_spec):
|
|
@@ -67,12 +68,10 @@ def test_query_validators__request__bad_endpoint(fx_openapi_3_2_0, fx_open_spec)
|
|
|
67
68
|
@pytest.mark.parametrize(
|
|
68
69
|
('spec', 'data'),
|
|
69
70
|
[
|
|
70
|
-
('no_params.yaml', {'headers': {'field': 'value'}}),
|
|
71
71
|
('only_headers.yaml', {'cookies': {'field': 'value'}}),
|
|
72
72
|
('only_headers.yaml', {'paths': {'field': 'value'}}),
|
|
73
73
|
('only_headers.yaml', {'queries': {'field': 'value'}}),
|
|
74
74
|
('only_headers.yaml', {'content_type': 'application/json', 'body': {'field': 'value'}}),
|
|
75
|
-
('only_query.yaml', {'headers': {'field': 'value'}}),
|
|
76
75
|
('only_headers.yaml', {'body': {'field': 'value'}}),
|
|
77
76
|
],
|
|
78
77
|
)
|
|
@@ -91,6 +90,59 @@ def test_query_validators__request_errors(fx_openapi_3_2_0, fx_open_spec, spec,
|
|
|
91
90
|
query_validator.request_handler(**request)
|
|
92
91
|
|
|
93
92
|
|
|
93
|
+
def test_query_validators__request__headers(fx_openapi_3_2_0, fx_open_spec):
|
|
94
|
+
file_path = fx_openapi_3_2_0('query_validator/only_headers.yaml')
|
|
95
|
+
spec = fx_open_spec(file_path)
|
|
96
|
+
query_validator = HTTPQueryValidator(spec)
|
|
97
|
+
|
|
98
|
+
request = {
|
|
99
|
+
'endpoint': '/endpoint',
|
|
100
|
+
'method': 'post',
|
|
101
|
+
'headers': {'header_field': 'value', 'header_field_1': 'value_1'},
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
serialized_request = query_validator.request_handler(**request)
|
|
105
|
+
|
|
106
|
+
assert serialized_request == request
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def test_query_validators__request__headers__errors(fx_openapi_3_2_0, fx_open_spec):
|
|
110
|
+
file_path = fx_openapi_3_2_0('query_validator/only_headers.yaml')
|
|
111
|
+
spec = fx_open_spec(file_path)
|
|
112
|
+
query_validator = HTTPQueryValidator(spec)
|
|
113
|
+
|
|
114
|
+
request = {
|
|
115
|
+
'endpoint': '/endpoint',
|
|
116
|
+
'method': 'post',
|
|
117
|
+
'headers': {'header_field': 1, 'header_field_2': 'value_2'},
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
with pytest.raises(RequestValidation) as exc:
|
|
121
|
+
query_validator.request_handler(**request)
|
|
122
|
+
|
|
123
|
+
assert exc.value.args[0] == (
|
|
124
|
+
"Request <{'endpoint': '/endpoint', 'method': 'post', 'headers': "
|
|
125
|
+
"{'header_field': 1, 'header_field_2': 'value_2'}}> validation error "
|
|
126
|
+
"<({'headers': {'header_field': ['Not a valid string.']}},)>."
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_query_validators__request__headers_not_in_spec(fx_openapi_3_2_0, fx_open_spec):
|
|
131
|
+
file_path = fx_openapi_3_2_0('query_validator/only_query.yaml')
|
|
132
|
+
spec = fx_open_spec(file_path)
|
|
133
|
+
query_validator = HTTPQueryValidator(spec)
|
|
134
|
+
|
|
135
|
+
request = {
|
|
136
|
+
'endpoint': '/endpoint',
|
|
137
|
+
'method': 'post',
|
|
138
|
+
'headers': {'header_field_1': 'value_1', 'header_field_2': 'value_2'},
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
serialized_request = query_validator.request_handler(**request)
|
|
142
|
+
|
|
143
|
+
assert serialized_request == request
|
|
144
|
+
|
|
145
|
+
|
|
94
146
|
def test_query_validators__response(fx_openapi_3_2_0, fx_open_spec):
|
|
95
147
|
file_path = fx_openapi_3_2_0('query_validator/openapi.yaml')
|
|
96
148
|
spec = fx_open_spec(file_path)
|
|
@@ -114,7 +166,12 @@ def test_query_validators__response__empty_body_in_spec(fx_openapi_3_2_0, fx_ope
|
|
|
114
166
|
spec = fx_open_spec(file_path)
|
|
115
167
|
query_validator = HTTPQueryValidator(spec)
|
|
116
168
|
|
|
117
|
-
request = {
|
|
169
|
+
request = {
|
|
170
|
+
'endpoint': '/endpoint',
|
|
171
|
+
'method': 'get',
|
|
172
|
+
'content_type': 'application/json',
|
|
173
|
+
'status_code': '204',
|
|
174
|
+
}
|
|
118
175
|
|
|
119
176
|
serialized_request = query_validator.response_handler(**request)
|
|
120
177
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|