Schema-First 0.14.0__tar.gz → 0.14.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.14.0 → schema_first-0.14.2}/PKG-INFO +2 -2
- {schema_first-0.14.0 → schema_first-0.14.2}/pyproject.toml +2 -2
- {schema_first-0.14.0 → schema_first-0.14.2}/src/Schema_First.egg-info/PKG-INFO +2 -2
- {schema_first-0.14.0 → schema_first-0.14.2}/src/Schema_First.egg-info/SOURCES.txt +1 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/loaders/yaml_loader.py +2 -2
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/fields.py +1 -1
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/v3_2/media_type_object_schema.py +1 -2
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/v3_2/openapi_object_schema.py +1 -1
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/specification/__init__.py +15 -9
- schema_first-0.14.2/src/schema_first/specification/spilli_api.py +65 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/tests/test_specification.py +4 -7
- {schema_first-0.14.0 → schema_first-0.14.2}/README.md +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/setup.cfg +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/Schema_First.egg-info/dependency_links.txt +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/Schema_First.egg-info/requires.txt +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/Schema_First.egg-info/top_level.txt +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/__init__.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/exceptions.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/loaders/__init__.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/loaders/exc.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/__init__.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/exc.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/__init__.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/base.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/constants.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/custom_fields.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/v3_2/components_object_schema.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/v3_2/contact_object_schema.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/v3_2/example_object_schema.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/v3_2/external_docs_object_schema.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/v3_2/header_object_schema.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/v3_2/info_object_schema.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/v3_2/license_object_schema.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/v3_2/operation_object_schema.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/v3_2/parameter_object_schema.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/v3_2/path_item_object_schema.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/v3_2/reference_object_schema.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/v3_2/request_body_object_schema.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/v3_2/responses_object_schema.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/v3_2/schema_object_schema.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/v3_2/security_scheme_object_schema.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/v3_2/server_object_schema.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/v3_2/server_variable_object_schema.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/v3_2/tag_object_schema.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/validators.py +0 -0
- {schema_first-0.14.0 → schema_first-0.14.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.14.
|
|
3
|
+
Version: 0.14.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
|
|
@@ -8,7 +8,7 @@ Project-URL: changelog, https://github.com/flask-pro/schema-first/blob/master/CH
|
|
|
8
8
|
Project-URL: repository, https://github.com/flask-pro/schema-first
|
|
9
9
|
Classifier: Operating System :: OS Independent
|
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
|
11
|
-
Requires-Python: >=3.
|
|
11
|
+
Requires-Python: >=3.14
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
Requires-Dist: marshmallow>=4.3.0
|
|
14
14
|
Requires-Dist: PyYAML>=6.0.3
|
|
@@ -19,8 +19,8 @@ license = "LGPL-2.1-only"
|
|
|
19
19
|
license-files = ["LICENSE.txt"]
|
|
20
20
|
name = "Schema-First"
|
|
21
21
|
readme = "README.md"
|
|
22
|
-
requires-python = ">=3.
|
|
23
|
-
version = "0.14.
|
|
22
|
+
requires-python = ">=3.14"
|
|
23
|
+
version = "0.14.2"
|
|
24
24
|
|
|
25
25
|
[project.optional-dependencies]
|
|
26
26
|
dev = [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Schema-First
|
|
3
|
-
Version: 0.14.
|
|
3
|
+
Version: 0.14.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
|
|
@@ -8,7 +8,7 @@ Project-URL: changelog, https://github.com/flask-pro/schema-first/blob/master/CH
|
|
|
8
8
|
Project-URL: repository, https://github.com/flask-pro/schema-first
|
|
9
9
|
Classifier: Operating System :: OS Independent
|
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
|
11
|
-
Requires-Python: >=3.
|
|
11
|
+
Requires-Python: >=3.14
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
Requires-Dist: marshmallow>=4.3.0
|
|
14
14
|
Requires-Dist: PyYAML>=6.0.3
|
|
@@ -39,5 +39,6 @@ src/schema_first/openapi/schemas/v3_2/server_object_schema.py
|
|
|
39
39
|
src/schema_first/openapi/schemas/v3_2/server_variable_object_schema.py
|
|
40
40
|
src/schema_first/openapi/schemas/v3_2/tag_object_schema.py
|
|
41
41
|
src/schema_first/specification/__init__.py
|
|
42
|
+
src/schema_first/specification/spilli_api.py
|
|
42
43
|
tests/test_specification.py
|
|
43
44
|
tests/test_validator.py
|
|
@@ -58,7 +58,7 @@ class YAMLReader:
|
|
|
58
58
|
else:
|
|
59
59
|
return
|
|
60
60
|
|
|
61
|
-
def load(self) ->
|
|
61
|
+
def load(self) -> YAMLReader:
|
|
62
62
|
root_file = self._yaml_to_dict(self.path)
|
|
63
63
|
self.store[self.root_file_name] = root_file
|
|
64
64
|
self.search_file(root_file)
|
|
@@ -130,7 +130,7 @@ class RefResolver:
|
|
|
130
130
|
|
|
131
131
|
return obj
|
|
132
132
|
|
|
133
|
-
def resolving(self) ->
|
|
133
|
+
def resolving(self) -> RefResolver:
|
|
134
134
|
root_file_path = self.yaml_reader.root_file_name
|
|
135
135
|
root_spec = self.yaml_reader.store[root_file_path]
|
|
136
136
|
self.resolved_spec = self._resolving_all_refs(root_file_path, root_spec)
|
|
@@ -2,7 +2,7 @@ from marshmallow import fields
|
|
|
2
2
|
from marshmallow import validate
|
|
3
3
|
|
|
4
4
|
ENDPOINT_FIELD = fields.String(
|
|
5
|
-
required=True, validate=validate.Regexp(r'
|
|
5
|
+
required=True, validate=validate.Regexp(r'^/[.a-zA-Z0-9/{}_-]*[?]{0,1}[a-zA-Z0-9/=&_-]*$')
|
|
6
6
|
)
|
|
7
7
|
HTTP_CODE_FIELD = fields.String(required=True, validate=validate.Regexp(r'^[1-5]{1}\d{2}|default$'))
|
|
8
8
|
SUMMARY_FIELD = fields.String(validate=validate.Length(min=1, max=150))
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
from marshmallow import fields
|
|
2
2
|
|
|
3
3
|
from ..base import BaseSchema
|
|
4
|
-
from .example_object_schema import ExampleObjectSchema
|
|
5
4
|
from .schema_object_schema import SchemaObjectSchema
|
|
6
5
|
|
|
7
6
|
|
|
8
7
|
class MediaTypeObjectSchema(BaseSchema):
|
|
9
8
|
schema = fields.Nested(SchemaObjectSchema)
|
|
10
9
|
itemSchema = fields.Nested(SchemaObjectSchema)
|
|
11
|
-
examples = fields.Dict(keys=fields.String(), values=fields.
|
|
10
|
+
examples = fields.Dict(keys=fields.String(), values=fields.Raw())
|
|
@@ -57,7 +57,7 @@ class OpenAPIObjectSchema(BaseSchema):
|
|
|
57
57
|
if param_names:
|
|
58
58
|
params_from_components = data['components'].get('parameters')
|
|
59
59
|
if params_from_components:
|
|
60
|
-
param_names_from_components = params_from_components.
|
|
60
|
+
param_names_from_components = params_from_components.keys()
|
|
61
61
|
|
|
62
62
|
if not set(param_names).issubset(set(param_names_from_components)):
|
|
63
63
|
raise ValidationError(
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from copy import deepcopy
|
|
2
1
|
from pathlib import Path
|
|
3
2
|
from typing import Any
|
|
4
3
|
|
|
@@ -7,8 +6,10 @@ from marshmallow import INCLUDE
|
|
|
7
6
|
from marshmallow import RAISE
|
|
8
7
|
from marshmallow import Schema
|
|
9
8
|
from marshmallow import validate
|
|
9
|
+
from marshmallow.fields import Field
|
|
10
10
|
|
|
11
|
-
from
|
|
11
|
+
from schema_first.openapi import OpenAPI
|
|
12
|
+
from schema_first.specification.spilli_api import ConverterOpenAPIToSpilliAPI
|
|
12
13
|
|
|
13
14
|
FIELDS_VIA_TYPES = {
|
|
14
15
|
'boolean': fields.Boolean,
|
|
@@ -34,6 +35,7 @@ FIELDS_VIA_FORMATS = {
|
|
|
34
35
|
class Specification:
|
|
35
36
|
def __init__(self, spec_file: Path | str):
|
|
36
37
|
self.openapi = OpenAPI(spec_file)
|
|
38
|
+
self.spilli_api = None
|
|
37
39
|
self.reassembly_spec = None
|
|
38
40
|
|
|
39
41
|
@staticmethod
|
|
@@ -132,13 +134,17 @@ class Specification:
|
|
|
132
134
|
|
|
133
135
|
return Schema.from_dict(marshmallow_schema)
|
|
134
136
|
|
|
135
|
-
def _convert_array_field(self, open_api_schema: dict) ->
|
|
137
|
+
def _convert_array_field(self, open_api_schema: dict) -> Field:
|
|
136
138
|
array_item_schema = open_api_schema['items']
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
139
|
+
if array_item_schema['type'] == 'object':
|
|
140
|
+
nested_field = self._convert_object_field(array_item_schema)
|
|
141
|
+
array_field = fields.Nested(nested_field, many=True)
|
|
142
|
+
else:
|
|
143
|
+
nested_field = FIELDS_VIA_TYPES[array_item_schema['type']]()
|
|
144
|
+
array_field = fields.List(nested_field)
|
|
145
|
+
return array_field
|
|
140
146
|
|
|
141
|
-
def _reassembly_of_schemas(self, obj: Any) ->
|
|
147
|
+
def _reassembly_of_schemas(self, obj: Any) -> None:
|
|
142
148
|
if isinstance(obj, dict):
|
|
143
149
|
for k, v in obj.items():
|
|
144
150
|
# Checking for object type is needed to skip already resolved schemes.
|
|
@@ -148,10 +154,10 @@ class Specification:
|
|
|
148
154
|
else:
|
|
149
155
|
self._reassembly_of_schemas(v)
|
|
150
156
|
|
|
151
|
-
def load(self) ->
|
|
157
|
+
def load(self) -> Specification:
|
|
152
158
|
self.openapi.load()
|
|
153
|
-
self.reassembly_spec = deepcopy(self.openapi.raw_spec)
|
|
154
159
|
|
|
160
|
+
self.reassembly_spec = ConverterOpenAPIToSpilliAPI(self.openapi.raw_spec).convert()
|
|
155
161
|
self._reassembly_of_schemas(self.reassembly_spec)
|
|
156
162
|
|
|
157
163
|
return self
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Any
|
|
3
|
+
import typing as t
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ConverterOpenAPIToSpilliAPI:
|
|
7
|
+
def __init__(self, open_api_spec: dict) -> None:
|
|
8
|
+
self.open_api_spec = open_api_spec
|
|
9
|
+
self.spilli_api_spec = None
|
|
10
|
+
|
|
11
|
+
def _convert_params_to_schema(
|
|
12
|
+
self, params: list[dict], place: t.Literal['header', 'cookie', 'path', 'query']
|
|
13
|
+
) -> Any | None:
|
|
14
|
+
schema = {
|
|
15
|
+
'type': 'object',
|
|
16
|
+
'additionalProperties': True,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
required = []
|
|
20
|
+
properties = {}
|
|
21
|
+
for param in params:
|
|
22
|
+
if param['in'] == place:
|
|
23
|
+
if param.get('required', False):
|
|
24
|
+
required.append(param['name'])
|
|
25
|
+
properties[param['name']] = param['schema']
|
|
26
|
+
|
|
27
|
+
if not properties:
|
|
28
|
+
return None
|
|
29
|
+
else:
|
|
30
|
+
schema['properties'] = properties
|
|
31
|
+
|
|
32
|
+
if required:
|
|
33
|
+
schema['required'] = required
|
|
34
|
+
|
|
35
|
+
return schema
|
|
36
|
+
|
|
37
|
+
def _params_list_to_dict(self, params: list[dict]) -> dict:
|
|
38
|
+
params_as_dict = {}
|
|
39
|
+
if headers := self._convert_params_to_schema(params, 'header'):
|
|
40
|
+
params_as_dict['headers'] = {'schema': headers}
|
|
41
|
+
if cookies := self._convert_params_to_schema(params, 'cookie'):
|
|
42
|
+
params_as_dict['cookies'] = {'schema': cookies}
|
|
43
|
+
if paths := self._convert_params_to_schema(params, 'path'):
|
|
44
|
+
params_as_dict['paths'] = {'schema': paths}
|
|
45
|
+
if queries := self._convert_params_to_schema(params, 'query'):
|
|
46
|
+
params_as_dict['queries'] = {'schema': queries}
|
|
47
|
+
|
|
48
|
+
return params_as_dict
|
|
49
|
+
|
|
50
|
+
def _make_to_spilli_api_format(self) -> None:
|
|
51
|
+
self.spilli_api_spec = json.loads(json.dumps(self.open_api_spec))
|
|
52
|
+
|
|
53
|
+
for _, methods in self.spilli_api_spec['paths'].items():
|
|
54
|
+
common_params = []
|
|
55
|
+
if 'parameters' in methods:
|
|
56
|
+
common_params = methods.pop('parameters', [])
|
|
57
|
+
|
|
58
|
+
for _, operation_obj in methods.items():
|
|
59
|
+
if 'parameters' in operation_obj:
|
|
60
|
+
method_params = common_params + operation_obj.pop('parameters', [])
|
|
61
|
+
operation_obj['parameters'] = self._params_list_to_dict(method_params)
|
|
62
|
+
|
|
63
|
+
def convert(self) -> dict:
|
|
64
|
+
self._make_to_spilli_api_format()
|
|
65
|
+
return self.spilli_api_spec
|
|
@@ -7,15 +7,12 @@ from openapi_spec_validator import validate as osv_validate
|
|
|
7
7
|
from openapi_spec_validator.readers import read_from_filename
|
|
8
8
|
import pytest
|
|
9
9
|
|
|
10
|
-
from src.schema_first.openapi import OpenAPI
|
|
11
10
|
from src.schema_first.specification import Specification
|
|
11
|
+
from tests.conftest import specs_base_dir
|
|
12
12
|
from tests.conftest import tests_dir_abspath
|
|
13
13
|
from tests.utils import get_schema_from_request
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
spec_file_paths = list(Path(specs_base_dir, 'v3.0').iterdir())
|
|
17
|
-
spec_file_paths.extend(list(Path(specs_base_dir, 'v3.1').iterdir()))
|
|
18
|
-
spec_file_paths.extend(list(Path(specs_base_dir, 'v3.2').iterdir()))
|
|
15
|
+
spec_file_paths = list(Path(specs_base_dir, 'v3.2').iterdir())
|
|
19
16
|
|
|
20
17
|
|
|
21
18
|
@pytest.mark.parametrize('fx', ['fx_spec_required', 'fx_spec_full'])
|
|
@@ -23,7 +20,7 @@ def test_specification(request, fx_spec_as_file, fx):
|
|
|
23
20
|
spec_file = fx_spec_as_file(request.getfixturevalue(fx))
|
|
24
21
|
spec = Specification(spec_file)
|
|
25
22
|
|
|
26
|
-
assert
|
|
23
|
+
assert spec.openapi
|
|
27
24
|
assert spec.reassembly_spec is None
|
|
28
25
|
|
|
29
26
|
spec.load()
|
|
@@ -61,7 +58,7 @@ def test_specification__multifile_required(fx_spec_as_file, fx_spec_required):
|
|
|
61
58
|
|
|
62
59
|
@pytest.mark.xfail(reason='Schema specification not fully realisation.')
|
|
63
60
|
def test_specification__multifile():
|
|
64
|
-
file_path = Path(tests_dir_abspath, '_contrib', 'specs', '
|
|
61
|
+
file_path = Path(tests_dir_abspath, '_contrib', 'specs', 'v3.2', 'multifile', 'openapi.yaml')
|
|
65
62
|
spec_as_dict, base_uri = read_from_filename(file_path)
|
|
66
63
|
osv_validate(spec_as_dict, base_uri=base_uri)
|
|
67
64
|
|
|
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
|
{schema_first-0.14.0 → schema_first-0.14.2}/src/schema_first/openapi/schemas/custom_fields.py
RENAMED
|
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
|