Schema-First 0.16.2__tar.gz → 0.16.4__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.2 → schema_first-0.16.4}/PKG-INFO +1 -1
- {schema_first-0.16.2 → schema_first-0.16.4}/pyproject.toml +3 -3
- {schema_first-0.16.2 → schema_first-0.16.4}/src/Schema_First.egg-info/PKG-INFO +1 -1
- schema_first-0.16.4/src/schema_first/openapi/schemas/base.py +15 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/schemas/v3_2/info_object_schema.py +5 -5
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/query/validator.py +6 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/specification/__init__.py +19 -8
- {schema_first-0.16.2 → schema_first-0.16.4}/tests/test_query_validators.py +18 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/tests/test_specification.py +1 -1
- schema_first-0.16.2/src/schema_first/openapi/schemas/base.py +0 -28
- {schema_first-0.16.2 → schema_first-0.16.4}/README.md +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/setup.cfg +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/Schema_First.egg-info/SOURCES.txt +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/Schema_First.egg-info/dependency_links.txt +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/Schema_First.egg-info/requires.txt +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/Schema_First.egg-info/top_level.txt +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/__init__.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/exceptions.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/loaders/__init__.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/loaders/exc.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/loaders/yaml_loader.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/__init__.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/exc.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/schemas/__init__.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/schemas/constants.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/schemas/fields.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/schemas/v3_2/components_object_schema.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/schemas/v3_2/contact_object_schema.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/schemas/v3_2/example_object_schema.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/schemas/v3_2/external_docs_object_schema.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/schemas/v3_2/header_object_schema.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/schemas/v3_2/license_object_schema.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/schemas/v3_2/media_type_object_schema.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/schemas/v3_2/openapi_object_schema.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/schemas/v3_2/operation_object_schema.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/schemas/v3_2/parameter_object_schema.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/schemas/v3_2/path_item_object_schema.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/schemas/v3_2/reference_object_schema.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/schemas/v3_2/request_body_object_schema.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/schemas/v3_2/responses_object_schema.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/schemas/v3_2/schema_object_schema.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/schemas/v3_2/security_scheme_object_schema.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/schemas/v3_2/server_object_schema.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/schemas/v3_2/server_variable_object_schema.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/schemas/v3_2/tag_object_schema.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/openapi/schemas/validators.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/query/exceptions.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/src/schema_first/specification/spilli_api.py +0 -0
- {schema_first-0.16.2 → schema_first-0.16.4}/tests/test_validator.py +0 -0
|
@@ -20,7 +20,7 @@ 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.4"
|
|
24
24
|
|
|
25
25
|
[project.optional-dependencies]
|
|
26
26
|
dev = [
|
|
@@ -40,7 +40,7 @@ changelog = "https://github.com/flask-pro/schema-first/blob/master/CHANGES.md"
|
|
|
40
40
|
repository = "https://github.com/flask-pro/schema-first"
|
|
41
41
|
|
|
42
42
|
[tool.bandit]
|
|
43
|
-
exclude_dirs = ["tests"]
|
|
43
|
+
exclude_dirs = ["tests", "venv"]
|
|
44
44
|
|
|
45
45
|
[tool.black]
|
|
46
46
|
extend-exclude = '''
|
|
@@ -51,7 +51,7 @@ extend-exclude = '''
|
|
|
51
51
|
include = '\.pyi?$'
|
|
52
52
|
line-length = 100
|
|
53
53
|
skip-string-normalization = true
|
|
54
|
-
target-version = ['
|
|
54
|
+
target-version = ['py312']
|
|
55
55
|
|
|
56
56
|
[tool.isort]
|
|
57
57
|
include_trailing_comma = true
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from marshmallow import RAISE
|
|
2
|
+
from marshmallow import Schema
|
|
3
|
+
|
|
4
|
+
from schema_first.openapi.schemas.fields import DESCRIPTION_FIELD
|
|
5
|
+
from schema_first.openapi.schemas.fields import SUMMARY_FIELD
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BaseSchema(Schema):
|
|
9
|
+
class Meta:
|
|
10
|
+
unknown = RAISE
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class DocStringFields(Schema):
|
|
14
|
+
summary = SUMMARY_FIELD
|
|
15
|
+
description = DESCRIPTION_FIELD
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
from marshmallow import fields
|
|
2
2
|
from marshmallow import validate
|
|
3
3
|
|
|
4
|
-
from
|
|
5
|
-
from
|
|
6
|
-
from
|
|
7
|
-
from .contact_object_schema import ContactObjectSchema
|
|
8
|
-
from .license_object_schema import LicenseObjectSchema
|
|
4
|
+
from schema_first.openapi.schemas.base import BaseSchema
|
|
5
|
+
from schema_first.openapi.schemas.base import DocStringFields
|
|
6
|
+
from schema_first.openapi.schemas.constants import RE_VERSION
|
|
7
|
+
from schema_first.openapi.schemas.v3_2.contact_object_schema import ContactObjectSchema
|
|
8
|
+
from schema_first.openapi.schemas.v3_2.license_object_schema import LicenseObjectSchema
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class InfoObjectSchema(DocStringFields, BaseSchema):
|
|
@@ -85,6 +85,12 @@ class HTTPQueryValidator:
|
|
|
85
85
|
body_schema = content_type.get('schema')
|
|
86
86
|
if body_schema:
|
|
87
87
|
response_fields['body'] = fields.Nested(body_schema)
|
|
88
|
+
elif 'application/octet-stream' in content:
|
|
89
|
+
response_fields['content_type'] = fields.String()
|
|
90
|
+
response_fields['body'] = fields.Raw()
|
|
91
|
+
else:
|
|
92
|
+
raise NotImplementedError
|
|
93
|
+
|
|
88
94
|
else:
|
|
89
95
|
# If body must be empty.
|
|
90
96
|
if data.get('content_type'):
|
|
@@ -8,6 +8,7 @@ from marshmallow import RAISE
|
|
|
8
8
|
from marshmallow import Schema
|
|
9
9
|
from marshmallow import validate
|
|
10
10
|
from marshmallow.fields import Field
|
|
11
|
+
from marshmallow.fields import Nested
|
|
11
12
|
|
|
12
13
|
from schema_first.openapi import OpenAPI
|
|
13
14
|
from schema_first.specification.spilli_api import ConverterOpenAPIToSpilliAPI
|
|
@@ -103,8 +104,8 @@ class Specification:
|
|
|
103
104
|
initialized_schema.required = field_schema.get('required', False)
|
|
104
105
|
return initialized_schema
|
|
105
106
|
|
|
106
|
-
def _convert_field_any_type(self, field_schema: dict):
|
|
107
|
-
|
|
107
|
+
def _convert_field_any_type(self, field_schema: dict, as_schema: bool = False):
|
|
108
|
+
_converters = {
|
|
108
109
|
'string': self._convert_string_field,
|
|
109
110
|
'boolean': self._convert_boolean_field,
|
|
110
111
|
'number': self._convert_number_field,
|
|
@@ -112,8 +113,14 @@ class Specification:
|
|
|
112
113
|
'object': self._convert_object_field,
|
|
113
114
|
'array': self._convert_array_field,
|
|
114
115
|
}
|
|
116
|
+
|
|
117
|
+
field_type = field_schema['type']
|
|
118
|
+
|
|
115
119
|
try:
|
|
116
|
-
|
|
120
|
+
if field_type in ['object']:
|
|
121
|
+
converted_field_schema = _converters[field_type](field_schema, as_schema=as_schema)
|
|
122
|
+
else:
|
|
123
|
+
converted_field_schema = _converters[field_type](field_schema)
|
|
117
124
|
except KeyError:
|
|
118
125
|
raise NotImplementedError(
|
|
119
126
|
f'Schema <{field_schema}> for type <{field_schema["type"]}> not be converted.'
|
|
@@ -121,7 +128,9 @@ class Specification:
|
|
|
121
128
|
|
|
122
129
|
return converted_field_schema
|
|
123
130
|
|
|
124
|
-
def _convert_object_field(
|
|
131
|
+
def _convert_object_field(
|
|
132
|
+
self, open_api_schema: dict, many: bool = False, as_schema: bool = False
|
|
133
|
+
) -> type[Schema] | Nested:
|
|
125
134
|
marshmallow_schema = {}
|
|
126
135
|
required_fields = open_api_schema.get('required', [])
|
|
127
136
|
for field_name, field_schema in open_api_schema['properties'].items():
|
|
@@ -136,13 +145,15 @@ class Specification:
|
|
|
136
145
|
else:
|
|
137
146
|
marshmallow_schema['unknown'] = INCLUDE
|
|
138
147
|
|
|
139
|
-
|
|
148
|
+
if as_schema:
|
|
149
|
+
return Schema.from_dict(marshmallow_schema)
|
|
150
|
+
|
|
151
|
+
return fields.Nested(Schema.from_dict(marshmallow_schema), many=many)
|
|
140
152
|
|
|
141
153
|
def _convert_array_field(self, open_api_schema: dict) -> Field:
|
|
142
154
|
array_item_schema = open_api_schema['items']
|
|
143
155
|
if array_item_schema['type'] == 'object':
|
|
144
|
-
|
|
145
|
-
array_field = fields.Nested(nested_field, many=True)
|
|
156
|
+
array_field = self._convert_object_field(array_item_schema, many=True)
|
|
146
157
|
else:
|
|
147
158
|
nested_field = FIELDS_VIA_TYPES[array_item_schema['type']]()
|
|
148
159
|
array_field = fields.List(nested_field)
|
|
@@ -154,7 +165,7 @@ class Specification:
|
|
|
154
165
|
# Checking for object type is needed to skip already resolved schemes.
|
|
155
166
|
# This is necessary because of passing variables by reference in Python.
|
|
156
167
|
if k == 'schema' and isinstance(v, dict):
|
|
157
|
-
obj[k] = self._convert_field_any_type(v)
|
|
168
|
+
obj[k] = self._convert_field_any_type(v, as_schema=True)
|
|
158
169
|
else:
|
|
159
170
|
self._reassembly_of_schemas(v)
|
|
160
171
|
|
|
@@ -178,6 +178,24 @@ def test_query_validators__response__empty_body_in_spec(fx_openapi_3_2_0, fx_ope
|
|
|
178
178
|
assert serialized_request == request
|
|
179
179
|
|
|
180
180
|
|
|
181
|
+
def test_query_validators__binary_response(fx_openapi_3_2_0, fx_open_spec):
|
|
182
|
+
file_path = fx_openapi_3_2_0('query_validator/binary_response.yaml')
|
|
183
|
+
spec = fx_open_spec(file_path)
|
|
184
|
+
query_validator = HTTPQueryValidator(spec)
|
|
185
|
+
|
|
186
|
+
request = {
|
|
187
|
+
'endpoint': '/endpoint',
|
|
188
|
+
'method': 'get',
|
|
189
|
+
'content_type': 'multipart/form-data',
|
|
190
|
+
'status_code': '200',
|
|
191
|
+
'body': b'content',
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
serialized_request = query_validator.response_handler(**request)
|
|
195
|
+
|
|
196
|
+
assert serialized_request == request
|
|
197
|
+
|
|
198
|
+
|
|
181
199
|
def test_query_validators__response__empty_body_in_spec_error(fx_openapi_3_2_0, fx_open_spec):
|
|
182
200
|
file_path = fx_openapi_3_2_0('query_validator/empty_body.yaml')
|
|
183
201
|
spec = fx_open_spec(file_path)
|
|
@@ -62,7 +62,7 @@ def test_specification__multifile_required(fx_spec_as_file, fx_spec_required):
|
|
|
62
62
|
|
|
63
63
|
@pytest.mark.xfail(reason='Schema specification not fully realisation.')
|
|
64
64
|
def test_specification__multifile():
|
|
65
|
-
file_path = Path(tests_dir_abspath, '
|
|
65
|
+
file_path = Path(tests_dir_abspath, 'specs', 'openapi', 'v3.2', 'multifile', 'openapi.yaml')
|
|
66
66
|
spec_as_dict, base_uri = read_from_filename(file_path)
|
|
67
67
|
osv_validate(spec_as_dict, base_uri=base_uri)
|
|
68
68
|
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
from marshmallow import RAISE
|
|
2
|
-
from marshmallow import Schema
|
|
3
|
-
from marshmallow import validates_schema
|
|
4
|
-
from marshmallow import ValidationError
|
|
5
|
-
|
|
6
|
-
from schema_first.openapi.schemas.fields import DESCRIPTION_FIELD
|
|
7
|
-
from schema_first.openapi.schemas.fields import SUMMARY_FIELD
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class BaseSchema(Schema):
|
|
11
|
-
class Meta:
|
|
12
|
-
unknown = RAISE
|
|
13
|
-
|
|
14
|
-
@validates_schema
|
|
15
|
-
def validate_ref(self, data, **kwargs) -> None:
|
|
16
|
-
if 'ref' in data:
|
|
17
|
-
ALLOWED_FIELDS = {'ref', 'description', 'summary'}
|
|
18
|
-
ALL_FIELDS = set(data)
|
|
19
|
-
if ALL_FIELDS.difference(ALLOWED_FIELDS):
|
|
20
|
-
raise ValidationError(
|
|
21
|
-
f"If there is a <'ref'> field, then only <{ALLOWED_FIELDS}>,"
|
|
22
|
-
f" but set <{ALL_FIELDS}>"
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class DocStringFields(Schema):
|
|
27
|
-
summary = SUMMARY_FIELD
|
|
28
|
-
description = DESCRIPTION_FIELD
|
|
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
|