essentials-openapi 1.3.0__tar.gz → 1.4.0__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.
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/CHANGELOG.md +27 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/PKG-INFO +1 -1
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/__init__.py +1 -1
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/common.py +6 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/common.py +18 -4
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/jinja.py +53 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/md.py +1 -1
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/__init__.py +165 -2
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/examples.py +13 -6
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_markdown/partial/components-responses.html +1 -1
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_markdown/partial/content-examples.html +1 -1
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_markdown/partial/external-docs.html +1 -1
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_markdown/partial/info.html +1 -1
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_markdown/partial/path-items.html +2 -2
- essentials_openapi-1.4.0/openapidocs/mk/v3/views_markdown/partial/request-parameters.html +9 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_markdown/partial/request-responses.html +1 -1
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_markdown/partial/schema-repr.html +2 -4
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_markdown/partial/type.html +4 -4
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_mkdocs/partial/components-responses.html +1 -1
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_mkdocs/partial/external-docs.html +1 -1
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_mkdocs/partial/info.html +1 -1
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_mkdocs/partial/path-items.html +2 -2
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_mkdocs/partial/request-parameters.html +2 -2
- essentials_openapi-1.4.0/openapidocs/mk/v3/views_mkdocs/partial/request-responses.html +42 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_mkdocs/partial/schema-repr.html +2 -4
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_mkdocs/partial/type.html +3 -1
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_plantuml_api/partial/schema-repr.html +1 -1
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_plantuml_schemas/partial/schema-repr.html +1 -1
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/v3.py +39 -6
- essentials_openapi-1.3.0/openapidocs/mk/v3/views_markdown/partial/request-parameters.html +0 -9
- essentials_openapi-1.3.0/openapidocs/mk/v3/views_mkdocs/partial/request-responses.html +0 -63
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/.gitignore +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/LICENSE +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/README.md +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/commands/__init__.py +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/commands/docs.py +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/logs.py +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/main.py +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/__init__.py +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/contents.py +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/generate.py +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/texts.py +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_markdown/README.md +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_markdown/__init__.py +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_markdown/layout.html +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_markdown/partial/components-parameters.html +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_markdown/partial/components-schemas.html +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_markdown/partial/components-security-schemes.html +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_markdown/partial/request-auth.html +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_markdown/partial/request-body.html +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_markdown/partial/servers.html +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_markdown/partial/tags.html +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_mkdocs/README.md +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_mkdocs/__init__.py +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_mkdocs/layout.html +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_mkdocs/partial/components-parameters.html +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_mkdocs/partial/components-schemas.html +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_mkdocs/partial/components-security-schemes.html +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_mkdocs/partial/content-examples.html +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_mkdocs/partial/request-auth.html +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_mkdocs/partial/request-body.html +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_mkdocs/partial/servers.html +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_mkdocs/partial/tags.html +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_plantuml_api/README.md +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_plantuml_api/layout.html +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_plantuml_schemas/README.md +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_plantuml_schemas/layout.html +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/utils/__init__.py +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/utils/source.py +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/utils/web.py +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/v2.py +0 -0
- {essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/pyproject.toml +0 -0
|
@@ -5,7 +5,34 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.4.0] - 2026-03-07 :copilot:
|
|
9
|
+
|
|
10
|
+
- Add OAS 3.1 support, cross-version warnings, and fix nullable spacing, by @dcode.
|
|
11
|
+
- Fix MARKDOWN style table separators to use minimum 3 hyphens (issue #39), reported by @michael-nok.
|
|
12
|
+
- Fix [#45](https://github.com/Neoteroi/essentials-openapi/issues/45): add support for displaying descriptions of schema properties, reported by @Maia-Everett.
|
|
13
|
+
- Fix [#30](https://github.com/Neoteroi/essentials-openapi/issues/30): raise an error
|
|
14
|
+
when trying to generate output from an older Swagger v2 specification file (these were
|
|
15
|
+
never supported as there was never a `/mk/v2/` namespace, intentionally).
|
|
16
|
+
- Fix [#35](https://github.com/Neoteroi/essentials-openapi/issues/35): group response
|
|
17
|
+
codes in a tab group on MkDocs output, reported by @Andre601.
|
|
18
|
+
- Fix [#47](https://github.com/Neoteroi/essentials-openapi/issues/47): remove `wordwrap`
|
|
19
|
+
filters from all templates as they break links and mermaid chart code blocks in
|
|
20
|
+
descriptions, reported by @ElementalWarrior.
|
|
21
|
+
- Fix [#49](https://github.com/Neoteroi/essentials-openapi/issues/49): support `$ref`
|
|
22
|
+
values of the form `file.yaml#/fragment/path` (external file with JSON Pointer
|
|
23
|
+
fragment), reported by @mbklein.
|
|
24
|
+
- Fix [#55](https://github.com/Neoteroi/essentials-openapi/issues/55): `jsonSchemaDialect`
|
|
25
|
+
is not required and should not have a default value.
|
|
26
|
+
- Fix [#60](https://github.com/Neoteroi/essentials-openapi/issues/60): resolve `$ref`
|
|
27
|
+
values in response headers pointing to `#/components/headers/...` to avoid
|
|
28
|
+
`UndefinedError` when rendering response tables, reported by @copiousfreetime.
|
|
29
|
+
- Fix [#64](https://github.com/Neoteroi/essentials-openapi/issues/64): use `examples`
|
|
30
|
+
array (JSON Schema draft 6+) as a fallback for auto-generated response examples;
|
|
31
|
+
also use `enum` values as examples for all scalar types (integer, number, boolean),
|
|
32
|
+
reported by @jan-ldwg.
|
|
33
|
+
|
|
8
34
|
## [1.3.0] - 2025-11-19
|
|
35
|
+
|
|
9
36
|
- Add support for passing custom Jinja2 templates as an argument, by @sindrehan.
|
|
10
37
|
|
|
11
38
|
## [1.2.1] - 2025-07-30
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: essentials-openapi
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: Classes to generate OpenAPI Documentation v3 and v2, in JSON and YAML.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Neoteroi/essentials-openapi
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/Neoteroi/essentials-openapi/issues
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
__version__ = "1.
|
|
1
|
+
__version__ = "1.4.0"
|
|
2
2
|
VERSION = __version__
|
|
@@ -82,6 +82,10 @@ def normalize_dict_factory(items: list[tuple[Any, Any]]) -> dict[str, Any]:
|
|
|
82
82
|
data["$ref"] = value
|
|
83
83
|
continue
|
|
84
84
|
|
|
85
|
+
if key == "defs":
|
|
86
|
+
data["$defs"] = value
|
|
87
|
+
continue
|
|
88
|
+
|
|
85
89
|
for handler in TYPES_HANDLERS:
|
|
86
90
|
value = handler.normalize(value)
|
|
87
91
|
|
|
@@ -129,6 +133,8 @@ def _asdict_inner(obj: Any, dict_factory: Callable[[Any], Any]) -> Any:
|
|
|
129
133
|
for k, v in obj.items()
|
|
130
134
|
)
|
|
131
135
|
else:
|
|
136
|
+
for handler in TYPES_HANDLERS:
|
|
137
|
+
obj = handler.normalize(obj)
|
|
132
138
|
return copy.deepcopy(obj)
|
|
133
139
|
|
|
134
140
|
|
|
@@ -33,17 +33,29 @@ def is_reference(data: object) -> bool:
|
|
|
33
33
|
return "$ref" in data
|
|
34
34
|
|
|
35
35
|
|
|
36
|
+
def _type_matches(type_val: Any, expected: str) -> bool:
|
|
37
|
+
"""
|
|
38
|
+
Returns True if type_val equals expected (OAS 3.0 string) or contains expected
|
|
39
|
+
(OAS 3.1 list).
|
|
40
|
+
"""
|
|
41
|
+
if isinstance(type_val, list):
|
|
42
|
+
return expected in type_val
|
|
43
|
+
return type_val == expected
|
|
44
|
+
|
|
45
|
+
|
|
36
46
|
def is_object_schema(data: object) -> bool:
|
|
37
47
|
"""
|
|
38
48
|
Returns a value indicating whether the given schema dictionary represents
|
|
39
49
|
an object schema.
|
|
40
50
|
|
|
41
|
-
|
|
51
|
+
Supports both OAS 3.0 (type: "object") and OAS 3.1 (type: ["object", ...]).
|
|
42
52
|
"""
|
|
43
53
|
if not isinstance(data, dict):
|
|
44
54
|
return False
|
|
45
55
|
data = cast(dict[str, object], data)
|
|
46
|
-
return data.get("type")
|
|
56
|
+
return _type_matches(data.get("type"), "object") and isinstance(
|
|
57
|
+
data.get("properties"), dict
|
|
58
|
+
)
|
|
47
59
|
|
|
48
60
|
|
|
49
61
|
def is_array_schema(data: object) -> bool:
|
|
@@ -51,12 +63,14 @@ def is_array_schema(data: object) -> bool:
|
|
|
51
63
|
Returns a value indicating whether the given schema dictionary represents
|
|
52
64
|
an array schema.
|
|
53
65
|
|
|
54
|
-
|
|
66
|
+
Supports both OAS 3.0 (type: "array") and OAS 3.1 (type: ["array", ...]).
|
|
55
67
|
"""
|
|
56
68
|
if not isinstance(data, dict):
|
|
57
69
|
return False
|
|
58
70
|
data = cast(dict[str, object], data)
|
|
59
|
-
return data.get("type")
|
|
71
|
+
return _type_matches(data.get("type"), "array") and isinstance(
|
|
72
|
+
data.get("items"), dict
|
|
73
|
+
)
|
|
60
74
|
|
|
61
75
|
|
|
62
76
|
def get_ref_type_name(reference: dict[str, str] | str) -> str:
|
|
@@ -21,6 +21,56 @@ from .common import DocumentsWriter, is_reference
|
|
|
21
21
|
from .md import normalize_link, write_table
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
def get_primary_type(type_val):
|
|
25
|
+
"""
|
|
26
|
+
Returns the primary (first non-null) type from a schema type value.
|
|
27
|
+
|
|
28
|
+
Handles both OAS 3.0 (string) and OAS 3.1 (list) type representations:
|
|
29
|
+
- "string" → "string"
|
|
30
|
+
- ["string", "null"] → "string"
|
|
31
|
+
- ["null"] → "null"
|
|
32
|
+
- ["string", "integer"] → "string"
|
|
33
|
+
"""
|
|
34
|
+
if not type_val:
|
|
35
|
+
return None
|
|
36
|
+
if isinstance(type_val, list):
|
|
37
|
+
non_null = [t for t in type_val if t != "null"]
|
|
38
|
+
return non_null[0] if non_null else "null"
|
|
39
|
+
return type_val
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def is_nullable_schema(schema) -> bool:
|
|
43
|
+
"""
|
|
44
|
+
Returns True if the given schema is nullable.
|
|
45
|
+
|
|
46
|
+
Handles both OAS 3.0 (nullable: true) and OAS 3.1 (type: [..., "null"]) patterns.
|
|
47
|
+
"""
|
|
48
|
+
if not isinstance(schema, dict):
|
|
49
|
+
return False
|
|
50
|
+
if schema.get("nullable"):
|
|
51
|
+
return True
|
|
52
|
+
type_val = schema.get("type")
|
|
53
|
+
if isinstance(type_val, list):
|
|
54
|
+
return "null" in type_val
|
|
55
|
+
return False
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def get_type_display(type_val) -> str:
|
|
59
|
+
"""
|
|
60
|
+
Returns a display string for a schema type value.
|
|
61
|
+
|
|
62
|
+
Handles both OAS 3.0 (string) and OAS 3.1 (list) type representations:
|
|
63
|
+
- "string" → "string"
|
|
64
|
+
- ["string", "null"] → "string | null"
|
|
65
|
+
- ["string", "integer"] → "string | integer"
|
|
66
|
+
"""
|
|
67
|
+
if not type_val:
|
|
68
|
+
return ""
|
|
69
|
+
if isinstance(type_val, list):
|
|
70
|
+
return " | ".join(str(t) for t in type_val)
|
|
71
|
+
return str(type_val)
|
|
72
|
+
|
|
73
|
+
|
|
24
74
|
def configure_filters(env: Environment):
|
|
25
75
|
env.filters.update(
|
|
26
76
|
{"route": highlight_params, "table": write_table, "link": normalize_link}
|
|
@@ -35,6 +85,9 @@ def configure_functions(env: Environment):
|
|
|
35
85
|
"scalar_types": {"string", "integer", "boolean", "number"},
|
|
36
86
|
"get_http_status_phrase": get_http_status_phrase,
|
|
37
87
|
"write_md_table": write_table,
|
|
88
|
+
"get_primary_type": get_primary_type,
|
|
89
|
+
"is_nullable_schema": is_nullable_schema,
|
|
90
|
+
"get_type_display": get_type_display,
|
|
38
91
|
}
|
|
39
92
|
|
|
40
93
|
env.globals.update(helpers)
|
|
@@ -49,7 +49,7 @@ def write_table_lines(
|
|
|
49
49
|
if write_headers:
|
|
50
50
|
# add separator line after headers
|
|
51
51
|
yield write_row(
|
|
52
|
-
["-" * column_len for column_len in columns_widths.values()],
|
|
52
|
+
["-" * max(3, column_len) for column_len in columns_widths.values()],
|
|
53
53
|
columns_widths,
|
|
54
54
|
padding,
|
|
55
55
|
indent,
|
|
@@ -25,6 +25,19 @@ from openapidocs.mk.texts import EnglishTexts, Texts
|
|
|
25
25
|
from openapidocs.mk.v3.examples import get_example_from_schema
|
|
26
26
|
from openapidocs.utils.source import read_from_source
|
|
27
27
|
|
|
28
|
+
_OAS31_KEYWORDS = frozenset(
|
|
29
|
+
{
|
|
30
|
+
"const",
|
|
31
|
+
"if",
|
|
32
|
+
"then",
|
|
33
|
+
"else",
|
|
34
|
+
"prefixItems",
|
|
35
|
+
"unevaluatedProperties",
|
|
36
|
+
"unevaluatedItems",
|
|
37
|
+
"$defs",
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
|
|
28
41
|
|
|
29
42
|
def _can_simplify_json(content_type) -> bool:
|
|
30
43
|
return "json" in content_type or content_type == "text/plain"
|
|
@@ -106,11 +119,110 @@ class OpenAPIV3DocumentationHandler:
|
|
|
106
119
|
custom_templates_path=templates_path,
|
|
107
120
|
)
|
|
108
121
|
self.doc = self.normalize_data(copy.deepcopy(doc))
|
|
122
|
+
self._warn_31_features_in_30_doc()
|
|
123
|
+
self._warn_30_features_in_31_doc()
|
|
109
124
|
|
|
110
125
|
@property
|
|
111
126
|
def source(self) -> str:
|
|
112
127
|
return self._source
|
|
113
128
|
|
|
129
|
+
def _collect_31_features(self, obj: object, found: set) -> None:
|
|
130
|
+
"""Recursively scans obj for OAS 3.1-specific features, collecting them in found."""
|
|
131
|
+
if not isinstance(obj, dict):
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
type_val = obj.get("type")
|
|
135
|
+
if isinstance(type_val, list):
|
|
136
|
+
found.add('type as list (e.g. ["string", "null"])')
|
|
137
|
+
|
|
138
|
+
for kw in _OAS31_KEYWORDS:
|
|
139
|
+
if kw in obj:
|
|
140
|
+
found.add(kw)
|
|
141
|
+
|
|
142
|
+
for kw in ("exclusiveMinimum", "exclusiveMaximum"):
|
|
143
|
+
val = obj.get(kw)
|
|
144
|
+
if (
|
|
145
|
+
val is not None
|
|
146
|
+
and isinstance(val, (int, float))
|
|
147
|
+
and not isinstance(val, bool)
|
|
148
|
+
):
|
|
149
|
+
found.add(f"{kw} as number")
|
|
150
|
+
|
|
151
|
+
for value in obj.values():
|
|
152
|
+
if isinstance(value, dict):
|
|
153
|
+
self._collect_31_features(value, found)
|
|
154
|
+
elif isinstance(value, list):
|
|
155
|
+
for item in value:
|
|
156
|
+
self._collect_31_features(item, found)
|
|
157
|
+
|
|
158
|
+
def _warn_31_features_in_30_doc(self) -> None:
|
|
159
|
+
"""
|
|
160
|
+
Emits a warning if OAS 3.1-specific features are detected in a document
|
|
161
|
+
that declares an OAS 3.0.x version.
|
|
162
|
+
"""
|
|
163
|
+
version = self.doc.get("openapi", "")
|
|
164
|
+
if not (isinstance(version, str) and version.startswith("3.0")):
|
|
165
|
+
return
|
|
166
|
+
|
|
167
|
+
found: set = set()
|
|
168
|
+
|
|
169
|
+
if "webhooks" in self.doc:
|
|
170
|
+
found.add("webhooks")
|
|
171
|
+
|
|
172
|
+
self._collect_31_features(self.doc, found)
|
|
173
|
+
|
|
174
|
+
if found:
|
|
175
|
+
feature_list = ", ".join(sorted(found))
|
|
176
|
+
warnings.warn(
|
|
177
|
+
f"OpenAPI document declares version {version!r} but uses "
|
|
178
|
+
f"OAS 3.1-specific features: {feature_list}. "
|
|
179
|
+
"Consider updating the `openapi` field to '3.1.0'.",
|
|
180
|
+
stacklevel=3,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
def _collect_30_features(self, obj: object, found: set) -> None:
|
|
184
|
+
"""Recursively scans obj for OAS 3.0-specific features, collecting them in found."""
|
|
185
|
+
if not isinstance(obj, dict):
|
|
186
|
+
return
|
|
187
|
+
|
|
188
|
+
# nullable: true is OAS 3.0 only — replaced by type: [..., "null"] in 3.1
|
|
189
|
+
if obj.get("nullable") is True:
|
|
190
|
+
found.add("nullable: true")
|
|
191
|
+
|
|
192
|
+
# boolean exclusiveMinimum/exclusiveMaximum are 3.0 semantics;
|
|
193
|
+
# in 3.1 they are numeric bounds
|
|
194
|
+
for kw in ("exclusiveMinimum", "exclusiveMaximum"):
|
|
195
|
+
if isinstance(obj.get(kw), bool):
|
|
196
|
+
found.add(f"{kw}: true/false (boolean)")
|
|
197
|
+
|
|
198
|
+
for value in obj.values():
|
|
199
|
+
if isinstance(value, dict):
|
|
200
|
+
self._collect_30_features(value, found)
|
|
201
|
+
elif isinstance(value, list):
|
|
202
|
+
for item in value:
|
|
203
|
+
self._collect_30_features(item, found)
|
|
204
|
+
|
|
205
|
+
def _warn_30_features_in_31_doc(self) -> None:
|
|
206
|
+
"""
|
|
207
|
+
Emits a warning if OAS 3.0-specific features are detected in a document
|
|
208
|
+
that declares an OAS 3.1.x version.
|
|
209
|
+
"""
|
|
210
|
+
version = self.doc.get("openapi", "")
|
|
211
|
+
if not (isinstance(version, str) and version.startswith("3.1")):
|
|
212
|
+
return
|
|
213
|
+
|
|
214
|
+
found: set = set()
|
|
215
|
+
self._collect_30_features(self.doc, found)
|
|
216
|
+
|
|
217
|
+
if found:
|
|
218
|
+
feature_list = ", ".join(sorted(found))
|
|
219
|
+
warnings.warn(
|
|
220
|
+
f"OpenAPI document declares version {version!r} but uses "
|
|
221
|
+
f"OAS 3.0-specific features: {feature_list}. "
|
|
222
|
+
"These features are not valid in OAS 3.1 and may be ignored by tooling.",
|
|
223
|
+
stacklevel=3,
|
|
224
|
+
)
|
|
225
|
+
|
|
114
226
|
def normalize_data(self, data):
|
|
115
227
|
"""
|
|
116
228
|
Applies corrections to the OpenAPI specification, to simplify its handling.
|
|
@@ -125,6 +237,13 @@ class OpenAPIV3DocumentationHandler:
|
|
|
125
237
|
$ref fields MUST be used in the specification to reference those parts as
|
|
126
238
|
follows from the JSON Schema definitions.
|
|
127
239
|
"""
|
|
240
|
+
if isinstance(data.get("swagger"), str) and data["swagger"].startswith("2"):
|
|
241
|
+
raise ValueError(
|
|
242
|
+
"Swagger 2.0 specifications are not supported. "
|
|
243
|
+
"Please convert your specification to OpenAPI 3.x first. "
|
|
244
|
+
"You can use the online converter at https://converter.swagger.io/"
|
|
245
|
+
)
|
|
246
|
+
|
|
128
247
|
if "components" not in data:
|
|
129
248
|
data["components"] = {}
|
|
130
249
|
|
|
@@ -156,18 +275,46 @@ class OpenAPIV3DocumentationHandler:
|
|
|
156
275
|
Handles a dictionary containing a $ref property, resolving the reference if it
|
|
157
276
|
is to a file. This is used to read specification files when they are split into
|
|
158
277
|
multiple items.
|
|
278
|
+
|
|
279
|
+
Supports three forms:
|
|
280
|
+
- ``#/internal/ref`` — internal ref, left as-is
|
|
281
|
+
- ``path/to/file.yaml`` — entire external file
|
|
282
|
+
- ``path/to/file.yaml#/fragment/path`` — fragment within an external file
|
|
159
283
|
"""
|
|
160
284
|
assert isinstance(obj, dict)
|
|
161
285
|
if "$ref" in obj:
|
|
162
286
|
reference = obj["$ref"]
|
|
163
287
|
if isinstance(reference, str) and not reference.startswith("#/"):
|
|
164
|
-
|
|
288
|
+
# Split off an optional JSON Pointer fragment (#/...)
|
|
289
|
+
if "#" in reference:
|
|
290
|
+
file_part, fragment = reference.split("#", 1)
|
|
291
|
+
else:
|
|
292
|
+
file_part, fragment = reference, ""
|
|
293
|
+
|
|
294
|
+
referred_file = Path(os.path.abspath(source_path / file_part))
|
|
165
295
|
|
|
166
296
|
if referred_file.exists():
|
|
167
297
|
logger.debug("Handling $ref source: %s", reference)
|
|
168
298
|
else:
|
|
169
299
|
raise OpenAPIFileNotFoundError(reference, referred_file)
|
|
300
|
+
|
|
170
301
|
sub_fragment = read_from_source(str(referred_file))
|
|
302
|
+
|
|
303
|
+
if fragment:
|
|
304
|
+
# Resolve the JSON Pointer (RFC 6901) into the loaded data.
|
|
305
|
+
# Strip the leading '/' then split on '/'.
|
|
306
|
+
keys = fragment.lstrip("/").split("/")
|
|
307
|
+
for key in keys:
|
|
308
|
+
if (
|
|
309
|
+
not isinstance(sub_fragment, dict)
|
|
310
|
+
or key not in sub_fragment
|
|
311
|
+
):
|
|
312
|
+
raise OpenAPIDocumentationHandlerError(
|
|
313
|
+
f"Cannot resolve fragment '{fragment}' in {referred_file}: "
|
|
314
|
+
f"key '{key}' not found."
|
|
315
|
+
)
|
|
316
|
+
sub_fragment = sub_fragment[key]
|
|
317
|
+
|
|
171
318
|
return self._transform_data(sub_fragment, referred_file.parent)
|
|
172
319
|
else:
|
|
173
320
|
return obj
|
|
@@ -179,7 +326,10 @@ class OpenAPIV3DocumentationHandler:
|
|
|
179
326
|
"""
|
|
180
327
|
data = self.doc
|
|
181
328
|
groups = defaultdict(list)
|
|
182
|
-
paths = data
|
|
329
|
+
paths = data.get("paths") # paths is optional in OAS 3.1
|
|
330
|
+
|
|
331
|
+
if not paths:
|
|
332
|
+
return groups
|
|
183
333
|
|
|
184
334
|
for path, path_item in paths.items():
|
|
185
335
|
if not isinstance(path_item, dict):
|
|
@@ -457,6 +607,19 @@ class OpenAPIV3DocumentationHandler:
|
|
|
457
607
|
|
|
458
608
|
return results
|
|
459
609
|
|
|
610
|
+
def get_response_headers(self, response_definition: dict) -> dict:
|
|
611
|
+
"""
|
|
612
|
+
Returns the headers of a response definition, resolving any $ref values
|
|
613
|
+
so that the template can access fields like schema and description directly.
|
|
614
|
+
"""
|
|
615
|
+
headers = response_definition.get("headers")
|
|
616
|
+
if not headers:
|
|
617
|
+
return {}
|
|
618
|
+
return {
|
|
619
|
+
name: self._resolve_opt_ref(header_def)
|
|
620
|
+
for name, header_def in headers.items()
|
|
621
|
+
}
|
|
622
|
+
|
|
460
623
|
def write(self) -> str:
|
|
461
624
|
return self._writer.write(
|
|
462
625
|
self.doc,
|
|
@@ -35,6 +35,10 @@ class ScalarExampleHandler(SchemaExampleHandler):
|
|
|
35
35
|
formats: Dict[str, Callable[[], Any]]
|
|
36
36
|
|
|
37
37
|
def get_example(self, schema) -> str:
|
|
38
|
+
enum = schema.get("enum")
|
|
39
|
+
if isinstance(enum, list) and enum:
|
|
40
|
+
return enum[0]
|
|
41
|
+
|
|
38
42
|
format = schema.get("format")
|
|
39
43
|
|
|
40
44
|
if format and format in self.formats:
|
|
@@ -56,12 +60,6 @@ class StringExampleHandler(ScalarExampleHandler):
|
|
|
56
60
|
"binary": lambda: "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ=",
|
|
57
61
|
}
|
|
58
62
|
|
|
59
|
-
def get_example(self, schema) -> str:
|
|
60
|
-
enum = schema.get("enum")
|
|
61
|
-
if isinstance(enum, list):
|
|
62
|
-
return enum[0]
|
|
63
|
-
return super().get_example(schema)
|
|
64
|
-
|
|
65
63
|
|
|
66
64
|
class IntegerExampleHandler(ScalarExampleHandler):
|
|
67
65
|
type_name = "integer"
|
|
@@ -138,6 +136,10 @@ def get_example_from_schema(schema) -> Any:
|
|
|
138
136
|
if "example" in schema:
|
|
139
137
|
return schema["example"]
|
|
140
138
|
|
|
139
|
+
examples = schema.get("examples")
|
|
140
|
+
if isinstance(examples, list) and examples:
|
|
141
|
+
return examples[0]
|
|
142
|
+
|
|
141
143
|
# does it have a type?
|
|
142
144
|
handlers_types: List[Type[SchemaExampleHandler]] = list(
|
|
143
145
|
get_subclasses(SchemaExampleHandler)
|
|
@@ -145,6 +147,11 @@ def get_example_from_schema(schema) -> Any:
|
|
|
145
147
|
|
|
146
148
|
schema_type = schema.get("type")
|
|
147
149
|
|
|
150
|
+
# OAS 3.1: type can be a list (e.g. ["string", "null"]). Use the first non-null type.
|
|
151
|
+
if isinstance(schema_type, list):
|
|
152
|
+
non_null = [t for t in schema_type if t != "null"]
|
|
153
|
+
schema_type = non_null[0] if non_null else None
|
|
154
|
+
|
|
148
155
|
if schema_type:
|
|
149
156
|
handler_type = next(
|
|
150
157
|
(_type for _type in handlers_types if _type.type_name == schema_type), None
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
{%- for name, definition in components.responses.items() %}
|
|
5
5
|
|
|
6
6
|
### {{name}}
|
|
7
|
-
{% if definition.description %}{{definition.description
|
|
7
|
+
{% if definition.description %}{{definition.description}}{% endif -%}
|
|
8
8
|
|
|
9
9
|
{%- if definition.content -%}
|
|
10
10
|
{% with content = handler.simplify_content(definition.content) -%}
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
```json
|
|
4
4
|
{{handler.write_content_example(example, content_type) | safe}}
|
|
5
5
|
```
|
|
6
|
-
{% if example.auto_generated %}_{{texts.auto_generated_example_note
|
|
6
|
+
{% if example.auto_generated %}_{{texts.auto_generated_example_note}}_{% endif -%}
|
|
7
7
|
{% endif %}
|
|
8
8
|
{% endfor %}
|
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
|
|
8
8
|
### {{http_method.upper()}} {{path | safe}}
|
|
9
9
|
{% if "summary" in operation -%}
|
|
10
|
-
{{operation.summary
|
|
10
|
+
{{operation.summary}}
|
|
11
11
|
{%- endif -%}
|
|
12
12
|
|
|
13
13
|
{%- if operation.description and operation.summary != operation.description %}
|
|
14
14
|
|
|
15
15
|
**{{texts.description.title()}}**
|
|
16
|
-
{{operation.description
|
|
16
|
+
{{operation.description}}
|
|
17
17
|
|
|
18
18
|
{%- endif -%}
|
|
19
19
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
|
|
2
|
+
**{{texts.parameters}}**
|
|
3
|
+
|
|
4
|
+
{% with rows = [[texts.parameter, texts.parameter_location, texts.type, texts.default, texts.nullable, texts.description]] %}
|
|
5
|
+
{%- for param in parameters -%}
|
|
6
|
+
{%- set _ = rows.append([param.name, param.in, get_type_display(read_dict(param, "schema", "type")), read_dict(param, "schema", "default", default=""), texts.get_yes_no(is_nullable_schema(read_dict(param, "schema") or {})), read_dict(param, "description", default="")]) -%}
|
|
7
|
+
{%- endfor -%}
|
|
8
|
+
{{ rows | table }}
|
|
9
|
+
{%- endwith -%}
|
|
@@ -25,7 +25,7 @@ Refer to the common response description: [{{type_name}}](#{{type_name.lower()}}
|
|
|
25
25
|
{%- if definition.headers %}
|
|
26
26
|
|
|
27
27
|
{% with rows = [[texts.name, texts.description, texts.schema]] %}
|
|
28
|
-
{%- for header_name, header_definition in definition.
|
|
28
|
+
{%- for header_name, header_definition in handler.get_response_headers(definition).items() -%}
|
|
29
29
|
{%- set _ = rows.append([header_name, header_definition.description, header_definition.schema.type]) -%}
|
|
30
30
|
{%- endfor -%}
|
|
31
31
|
{{ rows | table }}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
{%- endif -%}
|
|
6
6
|
|
|
7
7
|
{%- if schema.type -%}
|
|
8
|
-
{%- with type_name = schema["type"], nullable = schema
|
|
8
|
+
{%- with type_name = get_primary_type(schema["type"]), nullable = is_nullable_schema(schema) -%}
|
|
9
9
|
{%- if type_name == "object" -%}
|
|
10
10
|
{%- if schema.example -%}
|
|
11
11
|
_{{texts.example}}: _`{{schema.example}}`
|
|
@@ -19,9 +19,7 @@ _{{texts.properties}}: _`{{", ".join(schema.properties.keys())}}`
|
|
|
19
19
|
{%- if schema.format -%}
|
|
20
20
|
({{schema.format}})
|
|
21
21
|
{%- endif -%}
|
|
22
|
-
{%- if nullable -%}
|
|
23
|
-
| null
|
|
24
|
-
{%- endif -%}
|
|
22
|
+
{%- if nullable %} | null{%- endif -%}
|
|
25
23
|
{%- endif -%}
|
|
26
24
|
{%- if type_name == "array" -%}
|
|
27
25
|
{%- with schema = schema["items"] -%}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
{% if definition.type == "object" %}
|
|
1
|
+
{% if get_primary_type(definition.type) == "object" %}
|
|
2
2
|
{%- with props = handler.get_properties(definition) -%}
|
|
3
3
|
{% if props %}
|
|
4
|
-
| {{texts.name}} | {{texts.type}} |
|
|
5
|
-
|
|
|
4
|
+
| {{texts.name}} | {{texts.type}} | {{texts.description}} |
|
|
5
|
+
| --- | --- | --- |
|
|
6
6
|
{%- for name, schema in props %}
|
|
7
|
-
| {{name}} | {% include "partial/schema-repr.html" %} |
|
|
7
|
+
| {{name}} | {% include "partial/schema-repr.html" %} | {{schema.description}} |
|
|
8
8
|
{%- endfor -%}
|
|
9
9
|
{%- endif %}
|
|
10
10
|
{%- endwith -%}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
{% for name, definition in components.responses.items() %}
|
|
6
6
|
|
|
7
7
|
### {{name}}
|
|
8
|
-
{% if definition.description %}{{definition.description
|
|
8
|
+
{% if definition.description %}{{definition.description}}{% endif %}
|
|
9
9
|
|
|
10
10
|
{%- if definition.content %}
|
|
11
11
|
{% with content = handler.simplify_content(definition.content) %}
|
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
|
|
8
8
|
### <span class="http-{{http_method.lower()}}">{{http_method.upper()}}</span> {{path | route | safe}}
|
|
9
9
|
{% if "summary" in operation -%}
|
|
10
|
-
{{operation.summary
|
|
10
|
+
{{operation.summary}}
|
|
11
11
|
{%- endif -%}
|
|
12
12
|
|
|
13
13
|
{%- if operation.description and operation.summary != operation.description %}
|
|
14
14
|
|
|
15
15
|
??? note "{{texts.description.title()}}"
|
|
16
|
-
{{operation.description |
|
|
16
|
+
{{operation.description | indent(4)}}
|
|
17
17
|
{% endif %}
|
|
18
18
|
|
|
19
19
|
{%- with parameters = handler.get_parameters(operation) -%}
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
<tr>
|
|
18
18
|
<td class="parameter-name"><code>{{param.name}}</code></td>
|
|
19
19
|
<td>{{param.in}}</td>
|
|
20
|
-
<td>{{read_dict(param, "schema", "type")}}</td>
|
|
20
|
+
<td>{{get_type_display(read_dict(param, "schema", "type"))}}</td>
|
|
21
21
|
<td>{{read_dict(param, "schema", "default", default="")}}</td>
|
|
22
|
-
<td>{{texts.get_yes_no(read_dict(param, "schema"
|
|
22
|
+
<td>{{texts.get_yes_no(is_nullable_schema(read_dict(param, "schema") or {}))}}</td>
|
|
23
23
|
<td>{{read_dict(param, "description", default="")}}</td>
|
|
24
24
|
</tr>
|
|
25
25
|
{%- endfor %}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<p class="responses-title"><strong>{{texts.responses}}</strong></p>
|
|
2
|
+
|
|
3
|
+
{% for code, definition in operation.responses.items() %}
|
|
4
|
+
=== "{% if code == "default" %}{{texts.other_responses}}{% else %}{{code}}{% with phrase = get_http_status_phrase(code) %}{% if phrase %} {{ phrase }}{% endif %}{% endwith %}{% endif %}"
|
|
5
|
+
{%- if is_reference(definition) -%}
|
|
6
|
+
{%- with type_name = definition["$ref"].replace("#/components/responses/", "") %}
|
|
7
|
+
<div class="common-response"><p>Refer to the common response description: <a href="#{{type_name.lower() | link}}" class="ref-link">{{type_name}}</a>.</p></div>
|
|
8
|
+
{%- endwith -%}
|
|
9
|
+
{%- endif -%}
|
|
10
|
+
{%- if definition.content %}
|
|
11
|
+
{%- with content = handler.simplify_content(definition.content) %}
|
|
12
|
+
{% for content_type, definition in content.items() %}
|
|
13
|
+
=== "{{content_type}}"
|
|
14
|
+
{% for example in handler.get_content_examples(definition) %}
|
|
15
|
+
{% if example.value %}
|
|
16
|
+
```json
|
|
17
|
+
{{handler.write_content_example(example, content_type) | indent(8) | safe}}
|
|
18
|
+
```
|
|
19
|
+
{% if example.auto_generated %}<span class="small-note">⚠️</span> <em class="small-note warning">{{texts.auto_generated_example_note}}</em>{% endif -%}
|
|
20
|
+
{% endif %}
|
|
21
|
+
{% endfor %}
|
|
22
|
+
{% if "alt_types" in definition %}<em class="small-note alt-types">{{texts.other_possible_types}}: {{definition.alt_types | join(", ")}}</em>{% endif %}
|
|
23
|
+
|
|
24
|
+
??? hint "{{texts.schema_of_the_response_body}}"
|
|
25
|
+
```json
|
|
26
|
+
{{handler.write_content_schema(definition) | indent(12) | safe}}
|
|
27
|
+
```
|
|
28
|
+
{% endfor %}
|
|
29
|
+
{% endwith -%}
|
|
30
|
+
{% endif -%}
|
|
31
|
+
{%- if definition.headers %}
|
|
32
|
+
|
|
33
|
+
**{{texts.response_headers}}**
|
|
34
|
+
|
|
35
|
+
| {{texts.name}} | {{texts.description}} | {{texts.schema}} |
|
|
36
|
+
| --- | --- | --- |
|
|
37
|
+
{%- for header_name, header_definition in handler.get_response_headers(definition).items() %}
|
|
38
|
+
| `{{header_name}}` | {{header_definition.description or ""}} | {%- with schema = header_definition.schema %}{%- include "partial/schema-repr.html" -%}{%- endwith %} |
|
|
39
|
+
{%- endfor %}
|
|
40
|
+
|
|
41
|
+
{% endif -%}
|
|
42
|
+
{%- endfor -%}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
{%- endif -%}
|
|
6
6
|
|
|
7
7
|
{%- if schema.type -%}
|
|
8
|
-
{%- with type_name = schema["type"], nullable = schema
|
|
8
|
+
{%- with type_name = get_primary_type(schema["type"]), nullable = is_nullable_schema(schema) -%}
|
|
9
9
|
{%- if type_name == "object" -%}
|
|
10
10
|
{%- if schema.example -%}
|
|
11
11
|
<em>{{texts.example}}: </em><code>{{schema.example}}</code>
|
|
@@ -19,9 +19,7 @@
|
|
|
19
19
|
{%- if schema.format -%}
|
|
20
20
|
(<span class="{{schema.format}}-format format">{{schema.format}}</span>)
|
|
21
21
|
{%- endif -%}
|
|
22
|
-
{%- if nullable -%}
|
|
23
|
-
| <span class="null-type">null</span>
|
|
24
|
-
{%- endif -%}
|
|
22
|
+
{%- if nullable %} | <span class="null-type">null</span>{%- endif -%}
|
|
25
23
|
{%- endif -%}
|
|
26
24
|
{%- if type_name == "array" -%}
|
|
27
25
|
{%- with schema = schema["items"] -%}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
{% if definition.type == "object" %}
|
|
1
|
+
{% if get_primary_type(definition.type) == "object" %}
|
|
2
2
|
{%- with props = handler.get_properties(definition) -%}
|
|
3
3
|
{% if props %}
|
|
4
4
|
<table>
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
<tr>
|
|
7
7
|
<th>{{texts.name}}</th>
|
|
8
8
|
<th>{{texts.type}}</th>
|
|
9
|
+
<th>{{texts.description}}</th>
|
|
9
10
|
</tr>
|
|
10
11
|
</thead>
|
|
11
12
|
<tbody>
|
|
@@ -13,6 +14,7 @@
|
|
|
13
14
|
<tr>
|
|
14
15
|
<td><code>{{name}}</code></td>
|
|
15
16
|
<td>{% include "partial/schema-repr.html" %}</td>
|
|
17
|
+
<td>{{schema.description}}</td>
|
|
16
18
|
</tr>
|
|
17
19
|
{%- endfor %}
|
|
18
20
|
</tbody>
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
{%- endif -%}
|
|
6
6
|
|
|
7
7
|
{%- if schema.type -%}
|
|
8
|
-
{%- with type_name = schema["type"], nullable = schema
|
|
8
|
+
{%- with type_name = get_primary_type(schema["type"]), nullable = is_nullable_schema(schema) -%}
|
|
9
9
|
{# Scalar types #}
|
|
10
10
|
{%- if type_name in scalar_types -%}
|
|
11
11
|
{{type_name}}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
{%- endif -%}
|
|
6
6
|
|
|
7
7
|
{%- if schema.type -%}
|
|
8
|
-
{%- with type_name = schema["type"], nullable = schema
|
|
8
|
+
{%- with type_name = get_primary_type(schema["type"]), nullable = is_nullable_schema(schema) -%}
|
|
9
9
|
{# Scalar types #}
|
|
10
10
|
{%- if type_name in scalar_types -%}
|
|
11
11
|
{{type_name}}
|
|
@@ -350,8 +350,30 @@ class Schema(OpenAPIElement):
|
|
|
350
350
|
A list of schemas where at least one must apply.
|
|
351
351
|
one_of (list["Schema" | "Reference"] | None):
|
|
352
352
|
A list of schemas where exactly one must apply.
|
|
353
|
-
not_ (
|
|
353
|
+
not_ (None | "Schema" | "Reference"):
|
|
354
354
|
A schema that must not apply.
|
|
355
|
+
if_ (None | "Schema" | "Reference"):
|
|
356
|
+
If-then-else conditional schema (JSON Schema / OAS 3.1).
|
|
357
|
+
then_ (None | "Schema" | "Reference"):
|
|
358
|
+
Schema applied when if_ validates successfully.
|
|
359
|
+
else_ (None | "Schema" | "Reference"):
|
|
360
|
+
Schema applied when if_ fails to validate.
|
|
361
|
+
exclusive_maximum (float | None):
|
|
362
|
+
OAS 3.1 / JSON Schema: the exclusive upper bound for numeric values.
|
|
363
|
+
exclusive_minimum (float | None):
|
|
364
|
+
OAS 3.1 / JSON Schema: the exclusive lower bound for numeric values.
|
|
365
|
+
multiple_of (float | None):
|
|
366
|
+
The value must be a multiple of this number.
|
|
367
|
+
const (Any | None):
|
|
368
|
+
OAS 3.1 / JSON Schema: the value must be exactly this value.
|
|
369
|
+
prefix_items (list["Schema" | "Reference"] | None):
|
|
370
|
+
OAS 3.1 / JSON Schema: schemas for tuple validation of array items.
|
|
371
|
+
unevaluated_properties (None | bool | "Schema" | "Reference"):
|
|
372
|
+
OAS 3.1 / JSON Schema: controls handling of unevaluated properties.
|
|
373
|
+
unevaluated_items (None | bool | "Schema" | "Reference"):
|
|
374
|
+
OAS 3.1 / JSON Schema: controls handling of unevaluated array items.
|
|
375
|
+
defs (dict[str, "Schema" | "Reference"] | None):
|
|
376
|
+
OAS 3.1 / JSON Schema: locally-scoped schema definitions ($defs).
|
|
355
377
|
"""
|
|
356
378
|
|
|
357
379
|
type: None | str | ValueType | list[None | str | ValueType] = None
|
|
@@ -376,15 +398,26 @@ class Schema(OpenAPIElement):
|
|
|
376
398
|
unique_items: bool | None = None
|
|
377
399
|
maximum: float | None = None
|
|
378
400
|
minimum: float | None = None
|
|
401
|
+
exclusive_maximum: float | None = None
|
|
402
|
+
exclusive_minimum: float | None = None
|
|
403
|
+
multiple_of: float | None = None
|
|
379
404
|
nullable: bool | None = None
|
|
380
405
|
xml: XML | None = None
|
|
381
406
|
items: "None | Schema | Reference" = None
|
|
382
|
-
|
|
407
|
+
prefix_items: list["Schema | Reference"] | None = None
|
|
408
|
+
enum: list[Any] | None = None
|
|
409
|
+
const: Any | None = None
|
|
383
410
|
discriminator: Discriminator | None = None
|
|
384
411
|
all_of: list["Schema | Reference"] | None = None
|
|
385
412
|
any_of: list["Schema | Reference"] | None = None
|
|
386
413
|
one_of: list["Schema | Reference"] | None = None
|
|
387
|
-
not_:
|
|
414
|
+
not_: "None | Schema | Reference" = None
|
|
415
|
+
if_: "None | Schema | Reference" = None
|
|
416
|
+
then_: "None | Schema | Reference" = None
|
|
417
|
+
else_: "None | Schema | Reference" = None
|
|
418
|
+
unevaluated_properties: "None | bool | Schema | Reference" = None
|
|
419
|
+
unevaluated_items: "None | bool | Schema | Reference" = None
|
|
420
|
+
defs: dict[str, "Schema | Reference"] | None = None
|
|
388
421
|
|
|
389
422
|
|
|
390
423
|
@dataclass
|
|
@@ -924,8 +957,8 @@ class OpenAPI(OpenAPIRoot):
|
|
|
924
957
|
Attributes:
|
|
925
958
|
openapi (str): The semantic version number of the OpenAPI Specification version.
|
|
926
959
|
info (Info | None): Metadata about the API.
|
|
927
|
-
json_schema_dialect (str): The default value for the $schema keyword within Schema Objects contained
|
|
928
|
-
within this OAS document.
|
|
960
|
+
json_schema_dialect (str | None): The default value for the $schema keyword within Schema Objects contained
|
|
961
|
+
within this OAS document. Optional; if omitted, the dialect is not declared.
|
|
929
962
|
paths (dict[str, PathItem] | None): The available paths and operations for the API.
|
|
930
963
|
servers (list[Server] | None): An array of Server Objects that provide connectivity information
|
|
931
964
|
to a target server.
|
|
@@ -938,7 +971,7 @@ class OpenAPI(OpenAPIRoot):
|
|
|
938
971
|
|
|
939
972
|
openapi: str = "3.1.0"
|
|
940
973
|
info: Info | None = None
|
|
941
|
-
json_schema_dialect: str =
|
|
974
|
+
json_schema_dialect: str | None = None
|
|
942
975
|
paths: dict[str, PathItem] | None = None
|
|
943
976
|
servers: list[Server] | None = None
|
|
944
977
|
components: Components | None = None
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
**{{texts.parameters}}**
|
|
3
|
-
|
|
4
|
-
{% with rows = [[texts.parameter, texts.parameter_location, texts.type, texts.default, texts.nullable, texts.description]] %}
|
|
5
|
-
{%- for param in parameters -%}
|
|
6
|
-
{%- set _ = rows.append([param.name, param.in, read_dict(param, "schema", "type"), read_dict(param, "schema", "default", default=""), texts.get_yes_no(read_dict(param, "schema", "nullable", default=False)), read_dict(param, "description", default="")]) -%}
|
|
7
|
-
{%- endfor -%}
|
|
8
|
-
{{ rows | table }}
|
|
9
|
-
{%- endwith -%}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
{% for code, definition in operation.responses.items() %}
|
|
2
|
-
|
|
3
|
-
<p class="response-title">
|
|
4
|
-
{% if code == "default" -%}
|
|
5
|
-
<strong>{{texts.other_responses}}</strong>
|
|
6
|
-
{%- else -%}
|
|
7
|
-
<strong>Response <span class="response-code code-{{code}}">{{code}}</span>
|
|
8
|
-
{%- with phrase = get_http_status_phrase(code) -%}
|
|
9
|
-
{%- if phrase -%}
|
|
10
|
-
<span class="status-phrase">{{ phrase }}</span>
|
|
11
|
-
{%- endif -%}
|
|
12
|
-
{%- endwith -%}
|
|
13
|
-
</strong>
|
|
14
|
-
{%- endif %}
|
|
15
|
-
</p>
|
|
16
|
-
{%- if is_reference(definition) -%}
|
|
17
|
-
{%- with type_name = definition["$ref"].replace("#/components/responses/", "") %}
|
|
18
|
-
<div class="common-response"><p>Refer to the common response description: <a href="#{{type_name.lower() | link}}" class="ref-link">{{type_name}}</a>.</p></div>
|
|
19
|
-
{%- endwith -%}
|
|
20
|
-
{%- endif -%}
|
|
21
|
-
{%- if definition.content %}
|
|
22
|
-
{%- with content = handler.simplify_content(definition.content) %}
|
|
23
|
-
{% for content_type, definition in content.items() %}
|
|
24
|
-
=== "{{content_type}}"
|
|
25
|
-
{% include "partial/content-examples.html" %}
|
|
26
|
-
{% if "alt_types" in definition %}<em class="small-note alt-types">{{texts.other_possible_types}}: {{definition.alt_types | join(", ")}}</em>{% endif %}
|
|
27
|
-
|
|
28
|
-
??? hint "{{texts.schema_of_the_response_body}}"
|
|
29
|
-
```json
|
|
30
|
-
{{handler.write_content_schema(definition) | indent(8) | safe}}
|
|
31
|
-
```
|
|
32
|
-
{% endfor %}
|
|
33
|
-
{% endwith -%}
|
|
34
|
-
{% endif -%}
|
|
35
|
-
{%- if definition.headers %}
|
|
36
|
-
<div class="response-section">
|
|
37
|
-
<p class="response-headers sub-section-title">{{texts.response_headers}}</p>
|
|
38
|
-
|
|
39
|
-
<table>
|
|
40
|
-
<thead>
|
|
41
|
-
<tr>
|
|
42
|
-
<th>{{texts.name}}</th>
|
|
43
|
-
<th>{{texts.description}}</th>
|
|
44
|
-
<th>{{texts.schema}}</th>
|
|
45
|
-
</tr>
|
|
46
|
-
</thead>
|
|
47
|
-
<tbody>
|
|
48
|
-
{%- for header_name, header_definition in definition.headers.items() %}
|
|
49
|
-
<tr>
|
|
50
|
-
<td><code>{{header_name}}</code></td>
|
|
51
|
-
<td>{{header_definition.description}}</td>
|
|
52
|
-
<td>
|
|
53
|
-
{%- with schema = header_definition.schema %}
|
|
54
|
-
{%- include "partial/schema-repr.html" -%}
|
|
55
|
-
{% endwith -%}
|
|
56
|
-
</td>
|
|
57
|
-
</tr>
|
|
58
|
-
{%- endfor %}
|
|
59
|
-
</tbody>
|
|
60
|
-
</table>
|
|
61
|
-
</div>
|
|
62
|
-
{% endif -%}
|
|
63
|
-
{%- endfor -%}
|
|
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
|
{essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_markdown/README.md
RENAMED
|
File without changes
|
{essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_markdown/__init__.py
RENAMED
|
File without changes
|
{essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_markdown/layout.html
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
|
{essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_mkdocs/README.md
RENAMED
|
File without changes
|
{essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_mkdocs/__init__.py
RENAMED
|
File without changes
|
{essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_mkdocs/layout.html
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
|
{essentials_openapi-1.3.0 → essentials_openapi-1.4.0}/openapidocs/mk/v3/views_plantuml_api/README.md
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
|