jentic-openapi-datamodels 1.0.0a18__py3-none-any.whl → 1.0.0a20__py3-none-any.whl
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.
- jentic/apitools/openapi/datamodels/low/extractors.py +3 -3
- jentic/apitools/openapi/datamodels/low/v30/__init__.py +76 -0
- jentic/apitools/openapi/datamodels/low/v30/builders/__init__.py +312 -0
- jentic/apitools/openapi/datamodels/low/v30/callback.py +131 -0
- jentic/apitools/openapi/datamodels/low/v30/components.py +236 -0
- jentic/apitools/openapi/datamodels/low/v30/contact.py +4 -10
- jentic/apitools/openapi/datamodels/low/v30/discriminator.py +4 -9
- jentic/apitools/openapi/datamodels/low/v30/encoding.py +81 -0
- jentic/apitools/openapi/datamodels/low/v30/example.py +91 -0
- jentic/apitools/openapi/datamodels/low/v30/external_documentation.py +4 -10
- jentic/apitools/openapi/datamodels/low/v30/header.py +120 -0
- jentic/apitools/openapi/datamodels/low/v30/info.py +14 -23
- jentic/apitools/openapi/datamodels/low/v30/license.py +4 -10
- jentic/apitools/openapi/datamodels/low/v30/link.py +141 -0
- jentic/apitools/openapi/datamodels/low/v30/media_type.py +110 -0
- jentic/apitools/openapi/datamodels/low/v30/oauth_flow.py +4 -10
- jentic/apitools/openapi/datamodels/low/v30/oauth_flows.py +7 -15
- jentic/apitools/openapi/datamodels/low/v30/openapi.py +149 -0
- jentic/apitools/openapi/datamodels/low/v30/operation.py +134 -0
- jentic/apitools/openapi/datamodels/low/v30/parameter.py +123 -0
- jentic/apitools/openapi/datamodels/low/v30/path_item.py +125 -0
- jentic/apitools/openapi/datamodels/low/v30/paths.py +108 -0
- jentic/apitools/openapi/datamodels/low/v30/reference.py +5 -9
- jentic/apitools/openapi/datamodels/low/v30/request_body.py +108 -0
- jentic/apitools/openapi/datamodels/low/v30/response.py +104 -0
- jentic/apitools/openapi/datamodels/low/v30/responses.py +109 -0
- jentic/apitools/openapi/datamodels/low/v30/schema.py +81 -97
- jentic/apitools/openapi/datamodels/low/v30/security_requirement.py +14 -9
- jentic/apitools/openapi/datamodels/low/v30/security_scheme.py +42 -22
- jentic/apitools/openapi/datamodels/low/v30/server.py +111 -0
- jentic/apitools/openapi/datamodels/low/v30/server_variable.py +4 -10
- jentic/apitools/openapi/datamodels/low/v30/tag.py +8 -46
- jentic/apitools/openapi/datamodels/low/v30/xml.py +4 -10
- jentic/apitools/openapi/datamodels/low/v31/__init__.py +77 -0
- jentic/apitools/openapi/datamodels/low/v31/builders/__init__.py +347 -0
- jentic/apitools/openapi/datamodels/low/v31/callback.py +131 -0
- jentic/apitools/openapi/datamodels/low/v31/components.py +240 -0
- jentic/apitools/openapi/datamodels/low/v31/contact.py +61 -0
- jentic/apitools/openapi/datamodels/low/v31/discriminator.py +62 -0
- jentic/apitools/openapi/datamodels/low/v31/encoding.py +81 -0
- jentic/apitools/openapi/datamodels/low/v31/example.py +91 -0
- jentic/apitools/openapi/datamodels/low/v31/external_documentation.py +59 -0
- jentic/apitools/openapi/datamodels/low/v31/header.py +120 -0
- jentic/apitools/openapi/datamodels/low/v31/info.py +116 -0
- jentic/apitools/openapi/datamodels/low/v31/license.py +61 -0
- jentic/apitools/openapi/datamodels/low/v31/link.py +141 -0
- jentic/apitools/openapi/datamodels/low/v31/media_type.py +110 -0
- jentic/apitools/openapi/datamodels/low/v31/oauth_flow.py +65 -0
- jentic/apitools/openapi/datamodels/low/v31/oauth_flows.py +108 -0
- jentic/apitools/openapi/datamodels/low/v31/openapi.py +168 -0
- jentic/apitools/openapi/datamodels/low/v31/operation.py +133 -0
- jentic/apitools/openapi/datamodels/low/v31/parameter.py +123 -0
- jentic/apitools/openapi/datamodels/low/v31/path_item.py +125 -0
- jentic/apitools/openapi/datamodels/low/v31/paths.py +108 -0
- jentic/apitools/openapi/datamodels/low/v31/reference.py +65 -0
- jentic/apitools/openapi/datamodels/low/v31/request_body.py +108 -0
- jentic/apitools/openapi/datamodels/low/v31/response.py +104 -0
- jentic/apitools/openapi/datamodels/low/v31/responses.py +109 -0
- jentic/apitools/openapi/datamodels/low/v31/schema.py +498 -0
- jentic/apitools/openapi/datamodels/low/v31/security_requirement.py +106 -0
- jentic/apitools/openapi/datamodels/low/v31/security_scheme.py +129 -0
- jentic/apitools/openapi/datamodels/low/v31/server.py +111 -0
- jentic/apitools/openapi/datamodels/low/v31/server_variable.py +70 -0
- jentic/apitools/openapi/datamodels/low/v31/tag.py +63 -0
- jentic/apitools/openapi/datamodels/low/v31/xml.py +54 -0
- jentic_openapi_datamodels-1.0.0a20.dist-info/METADATA +379 -0
- jentic_openapi_datamodels-1.0.0a20.dist-info/RECORD +75 -0
- jentic/apitools/openapi/datamodels/low/model_builder.py +0 -129
- jentic_openapi_datamodels-1.0.0a18.dist-info/METADATA +0 -211
- jentic_openapi_datamodels-1.0.0a18.dist-info/RECORD +0 -27
- {jentic_openapi_datamodels-1.0.0a18.dist-info → jentic_openapi_datamodels-1.0.0a20.dist-info}/WHEEL +0 -0
- {jentic_openapi_datamodels-1.0.0a18.dist-info → jentic_openapi_datamodels-1.0.0a20.dist-info}/licenses/LICENSE +0 -0
- {jentic_openapi_datamodels-1.0.0a18.dist-info → jentic_openapi_datamodels-1.0.0a20.dist-info}/licenses/NOTICE +0 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from ruamel import yaml
|
|
5
|
+
|
|
6
|
+
from ..context import Context
|
|
7
|
+
from ..extractors import extract_extension_fields
|
|
8
|
+
from ..fields import fixed_field, fixed_fields
|
|
9
|
+
from ..sources import FieldSource, KeySource, ValueSource, YAMLInvalidValue, YAMLValue
|
|
10
|
+
from .oauth_flow import OAuthFlow
|
|
11
|
+
from .oauth_flow import build as build_oauth_flow
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
__all__ = ["OAuthFlows", "build"]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True, slots=True)
|
|
18
|
+
class OAuthFlows:
|
|
19
|
+
"""
|
|
20
|
+
OAuth Flows Object representation for OpenAPI 3.1.
|
|
21
|
+
|
|
22
|
+
A container for the list of possible OAuth 2.0 authorization flows.
|
|
23
|
+
Used within Security Scheme Objects when type is set to "oauth2".
|
|
24
|
+
|
|
25
|
+
Attributes:
|
|
26
|
+
root_node: The top-level node representing the entire OAuth Flows object in the original source file
|
|
27
|
+
implicit: Configuration for the OAuth Implicit flow
|
|
28
|
+
password: Configuration for the OAuth Resource Owner Password flow
|
|
29
|
+
client_credentials: Configuration for the OAuth Client Credentials flow (application flow)
|
|
30
|
+
authorization_code: Configuration for the OAuth Authorization Code flow (three-legged OAuth)
|
|
31
|
+
extensions: Specification extensions (x-* fields)
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
root_node: yaml.Node
|
|
35
|
+
implicit: FieldSource[OAuthFlow] | None = fixed_field()
|
|
36
|
+
password: FieldSource[OAuthFlow] | None = fixed_field()
|
|
37
|
+
client_credentials: FieldSource[OAuthFlow] | None = fixed_field(
|
|
38
|
+
metadata={"yaml_name": "clientCredentials"}
|
|
39
|
+
)
|
|
40
|
+
authorization_code: FieldSource[OAuthFlow] | None = fixed_field(
|
|
41
|
+
metadata={"yaml_name": "authorizationCode"}
|
|
42
|
+
)
|
|
43
|
+
extensions: dict[KeySource[str], ValueSource[YAMLValue]] = field(default_factory=dict)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def build(
|
|
47
|
+
root: yaml.Node, context: Context | None = None
|
|
48
|
+
) -> OAuthFlows | ValueSource[YAMLInvalidValue]:
|
|
49
|
+
"""
|
|
50
|
+
Build an OAuthFlows object from a YAML node.
|
|
51
|
+
|
|
52
|
+
Preserves all source data as-is, regardless of type. This is a low-level/plumbing
|
|
53
|
+
model that provides complete source fidelity for inspection and validation.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
root: The YAML node to parse (should be a MappingNode)
|
|
57
|
+
context: Optional parsing context. If None, a default context will be created.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
An OAuthFlows object if the node is valid, or a ValueSource containing
|
|
61
|
+
the invalid data if the root is not a MappingNode (preserving the invalid data
|
|
62
|
+
and its source location for validation).
|
|
63
|
+
|
|
64
|
+
Example:
|
|
65
|
+
from ruamel.yaml import YAML
|
|
66
|
+
yaml = YAML()
|
|
67
|
+
root = yaml.compose("implicit:\\n authorizationUrl: https://example.com/auth\\n scopes: {}")
|
|
68
|
+
flows = build(root)
|
|
69
|
+
assert flows.implicit.value.authorization_url.value == 'https://example.com/auth'
|
|
70
|
+
"""
|
|
71
|
+
context = context or Context()
|
|
72
|
+
|
|
73
|
+
if not isinstance(root, yaml.MappingNode):
|
|
74
|
+
# Preserve invalid root data instead of returning None
|
|
75
|
+
value = context.yaml_constructor.construct_object(root, deep=True)
|
|
76
|
+
return ValueSource(value=value, value_node=root)
|
|
77
|
+
|
|
78
|
+
# Get fixed specification fields for this dataclass type
|
|
79
|
+
_fixed_fields = fixed_fields(OAuthFlows)
|
|
80
|
+
|
|
81
|
+
# Build YAML name to Python field name mapping
|
|
82
|
+
yaml_to_field = {
|
|
83
|
+
field.metadata.get("yaml_name", fname): fname for fname, field in _fixed_fields.items()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# Extract field values in a single pass
|
|
87
|
+
field_values: dict[str, Any] = {}
|
|
88
|
+
for key_node, value_node in root.value:
|
|
89
|
+
key = context.yaml_constructor.construct_yaml_str(key_node)
|
|
90
|
+
|
|
91
|
+
# Map YAML key to Python field name
|
|
92
|
+
field_name = yaml_to_field.get(key)
|
|
93
|
+
if field_name:
|
|
94
|
+
# Build OAuthFlow for this field - child builder handles invalid nodes
|
|
95
|
+
# FieldSource will auto-unwrap ValueSource if child returns it for invalid data
|
|
96
|
+
oauth_flow = build_oauth_flow(value_node, context=context)
|
|
97
|
+
field_values[field_name] = FieldSource(
|
|
98
|
+
value=oauth_flow, key_node=key_node, value_node=value_node
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
return OAuthFlows(
|
|
102
|
+
root_node=root,
|
|
103
|
+
implicit=field_values.get("implicit"),
|
|
104
|
+
password=field_values.get("password"),
|
|
105
|
+
client_credentials=field_values.get("client_credentials"),
|
|
106
|
+
authorization_code=field_values.get("authorization_code"),
|
|
107
|
+
extensions=extract_extension_fields(root, context),
|
|
108
|
+
)
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
from dataclasses import dataclass, field, replace
|
|
2
|
+
|
|
3
|
+
from ruamel import yaml
|
|
4
|
+
|
|
5
|
+
from ..context import Context
|
|
6
|
+
from ..fields import fixed_field
|
|
7
|
+
from ..sources import FieldSource, KeySource, ValueSource, YAMLInvalidValue, YAMLValue
|
|
8
|
+
from .builders import build_field_source, build_model
|
|
9
|
+
from .components import Components
|
|
10
|
+
from .components import build as build_components
|
|
11
|
+
from .external_documentation import ExternalDocumentation
|
|
12
|
+
from .info import Info
|
|
13
|
+
from .info import build as build_info
|
|
14
|
+
from .path_item import PathItem
|
|
15
|
+
from .paths import Paths
|
|
16
|
+
from .paths import build as build_paths
|
|
17
|
+
from .security_requirement import SecurityRequirement
|
|
18
|
+
from .server import Server
|
|
19
|
+
from .tag import Tag
|
|
20
|
+
from .tag import build as build_tag
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
__all__ = ["OpenAPI31", "build"]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True, slots=True)
|
|
27
|
+
class OpenAPI31:
|
|
28
|
+
"""
|
|
29
|
+
OpenAPI Object representation for OpenAPI 3.1.
|
|
30
|
+
|
|
31
|
+
This is the root document object of the OpenAPI document.
|
|
32
|
+
|
|
33
|
+
Note: This class uses full duplication (not inheritance from OpenAPI30) to enable precise
|
|
34
|
+
version detection via isinstance() checks. This is critical for validators and converters
|
|
35
|
+
that need to distinguish between OpenAPI 3.0 and 3.1 specifications, as the two versions
|
|
36
|
+
have different validation rules (e.g., paths is required in 3.0 but optional in 3.1).
|
|
37
|
+
|
|
38
|
+
Attributes:
|
|
39
|
+
root_node: The top-level node representing the entire OpenAPI object in the original source file
|
|
40
|
+
openapi: REQUIRED. The version number of the OpenAPI Specification that the document uses.
|
|
41
|
+
Must match the specification version (e.g., "3.1.0", "3.1.1", "3.1.2").
|
|
42
|
+
info: REQUIRED. Provides metadata about the API (title, version, description, etc.)
|
|
43
|
+
json_schema_dialect: The default value for the $schema keyword within Schema Objects in this OAS document.
|
|
44
|
+
servers: An array of Server Objects providing connectivity information to target servers
|
|
45
|
+
paths: The available paths and operations for the API. In OpenAPI 3.1, this is no longer strictly
|
|
46
|
+
required if components or webhooks are present.
|
|
47
|
+
webhooks: The incoming webhooks that MAY be received as part of this API and that the API consumer
|
|
48
|
+
MAY choose to implement. A map of PathItem Objects.
|
|
49
|
+
components: An element to hold reusable objects for different aspects of the OAS
|
|
50
|
+
security: A declaration of which security mechanisms can be used across the API
|
|
51
|
+
tags: A list of tags used by the specification with additional metadata
|
|
52
|
+
external_docs: Additional external documentation
|
|
53
|
+
extensions: Specification extensions (x-* fields)
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
root_node: yaml.Node
|
|
57
|
+
openapi: FieldSource[str] | None = fixed_field()
|
|
58
|
+
info: FieldSource[Info] | None = fixed_field()
|
|
59
|
+
json_schema_dialect: FieldSource[str] | None = fixed_field(
|
|
60
|
+
metadata={"yaml_name": "jsonSchemaDialect"}
|
|
61
|
+
)
|
|
62
|
+
servers: FieldSource[list["Server"]] | None = fixed_field()
|
|
63
|
+
paths: FieldSource[Paths] | None = fixed_field()
|
|
64
|
+
webhooks: FieldSource[dict[KeySource[str], "PathItem"]] | None = fixed_field()
|
|
65
|
+
components: FieldSource[Components] | None = fixed_field()
|
|
66
|
+
security: FieldSource[list["SecurityRequirement"]] | None = fixed_field()
|
|
67
|
+
tags: FieldSource[list[Tag]] | None = fixed_field()
|
|
68
|
+
external_docs: FieldSource["ExternalDocumentation"] | None = fixed_field(
|
|
69
|
+
metadata={"yaml_name": "externalDocs"}
|
|
70
|
+
)
|
|
71
|
+
extensions: dict[KeySource[str], ValueSource[YAMLValue]] = field(default_factory=dict)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def build(
|
|
75
|
+
root: yaml.Node, context: Context | None = None
|
|
76
|
+
) -> OpenAPI31 | ValueSource[YAMLInvalidValue]:
|
|
77
|
+
"""
|
|
78
|
+
Build an OpenAPI 3.1 object from a YAML node.
|
|
79
|
+
|
|
80
|
+
Preserves all source data as-is, regardless of type. This is a low-level/plumbing
|
|
81
|
+
model that provides complete source fidelity for inspection and validation.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
root: The YAML node to parse (should be a MappingNode)
|
|
85
|
+
context: Optional parsing context. If None, a default context will be created.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
An OpenAPI31 object if the node is valid, or a ValueSource containing
|
|
89
|
+
the invalid data if the root is not a MappingNode (preserving the invalid data
|
|
90
|
+
and its source location for validation).
|
|
91
|
+
|
|
92
|
+
Example:
|
|
93
|
+
from ruamel.yaml import YAML
|
|
94
|
+
yaml = YAML()
|
|
95
|
+
root = yaml.compose('''
|
|
96
|
+
openapi: 3.1.2
|
|
97
|
+
info:
|
|
98
|
+
title: Sample API
|
|
99
|
+
version: 1.0.0
|
|
100
|
+
paths:
|
|
101
|
+
/users:
|
|
102
|
+
get:
|
|
103
|
+
responses:
|
|
104
|
+
'200':
|
|
105
|
+
description: Success
|
|
106
|
+
webhooks:
|
|
107
|
+
newPet:
|
|
108
|
+
post:
|
|
109
|
+
requestBody:
|
|
110
|
+
description: Pet to add
|
|
111
|
+
''')
|
|
112
|
+
openapi_doc = build(root)
|
|
113
|
+
assert openapi_doc.openapi.value == '3.1.2'
|
|
114
|
+
assert openapi_doc.info.value.title.value == 'Sample API'
|
|
115
|
+
"""
|
|
116
|
+
context = context or Context()
|
|
117
|
+
|
|
118
|
+
# Use build_model for initial construction
|
|
119
|
+
openapi_obj = build_model(root, OpenAPI31, context=context)
|
|
120
|
+
|
|
121
|
+
# If build_model returned ValueSource (invalid node), return it immediately
|
|
122
|
+
if not isinstance(openapi_obj, OpenAPI31):
|
|
123
|
+
return openapi_obj
|
|
124
|
+
|
|
125
|
+
# Manually handle nested complex fields and list fields with custom builders
|
|
126
|
+
replacements = {}
|
|
127
|
+
for key_node, value_node in root.value:
|
|
128
|
+
key = context.yaml_constructor.construct_yaml_str(key_node)
|
|
129
|
+
|
|
130
|
+
if key == "info":
|
|
131
|
+
# Handle info field - Info object
|
|
132
|
+
replacements["info"] = FieldSource(
|
|
133
|
+
value=build_info(value_node, context), key_node=key_node, value_node=value_node
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
elif key == "paths":
|
|
137
|
+
# Handle paths field - Paths object
|
|
138
|
+
replacements["paths"] = FieldSource(
|
|
139
|
+
value=build_paths(value_node, context), key_node=key_node, value_node=value_node
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
elif key == "components":
|
|
143
|
+
# Handle components field - Components object
|
|
144
|
+
replacements["components"] = FieldSource(
|
|
145
|
+
value=build_components(value_node, context),
|
|
146
|
+
key_node=key_node,
|
|
147
|
+
value_node=value_node,
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
elif key == "tags":
|
|
151
|
+
# Handle tags field - array of Tag objects
|
|
152
|
+
if isinstance(value_node, yaml.SequenceNode):
|
|
153
|
+
tags_list = []
|
|
154
|
+
for tag_node in value_node.value:
|
|
155
|
+
tag_obj = build_tag(tag_node, context)
|
|
156
|
+
tags_list.append(tag_obj)
|
|
157
|
+
replacements["tags"] = FieldSource(
|
|
158
|
+
value=tags_list, key_node=key_node, value_node=value_node
|
|
159
|
+
)
|
|
160
|
+
else:
|
|
161
|
+
# Not a sequence - preserve as-is for validation
|
|
162
|
+
replacements["tags"] = build_field_source(key_node, value_node, context)
|
|
163
|
+
|
|
164
|
+
# Apply all replacements at once
|
|
165
|
+
if replacements:
|
|
166
|
+
openapi_obj = replace(openapi_obj, **replacements)
|
|
167
|
+
|
|
168
|
+
return openapi_obj
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
from dataclasses import dataclass, field, replace
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
from ruamel import yaml
|
|
5
|
+
|
|
6
|
+
from ..context import Context
|
|
7
|
+
from ..fields import fixed_field
|
|
8
|
+
from ..sources import FieldSource, KeySource, ValueSource, YAMLInvalidValue, YAMLValue
|
|
9
|
+
from .builders import build_model
|
|
10
|
+
from .external_documentation import ExternalDocumentation
|
|
11
|
+
from .parameter import Parameter
|
|
12
|
+
from .reference import Reference
|
|
13
|
+
from .request_body import RequestBody, build_request_body_or_reference
|
|
14
|
+
from .responses import Responses
|
|
15
|
+
from .responses import build as build_responses
|
|
16
|
+
from .security_requirement import SecurityRequirement
|
|
17
|
+
from .server import Server
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from .callback import Callback
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
__all__ = ["Operation", "build"]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(frozen=True, slots=True)
|
|
28
|
+
class Operation:
|
|
29
|
+
"""
|
|
30
|
+
Operation Object representation for OpenAPI 3.1.
|
|
31
|
+
|
|
32
|
+
Describes a single API operation on a path.
|
|
33
|
+
|
|
34
|
+
Attributes:
|
|
35
|
+
root_node: The top-level node representing the entire Operation object in the original source file
|
|
36
|
+
tags: List of tags for API documentation control
|
|
37
|
+
summary: Short summary of what the operation does
|
|
38
|
+
description: Verbose explanation of the operation behavior (may contain CommonMark syntax)
|
|
39
|
+
external_docs: Additional external documentation for this operation
|
|
40
|
+
operation_id: Unique string used to identify the operation
|
|
41
|
+
parameters: List of parameters that are applicable for this operation
|
|
42
|
+
request_body: Request body applicable for this operation
|
|
43
|
+
responses: REQUIRED. The list of possible responses as they are returned from executing this operation
|
|
44
|
+
callbacks: Map of possible out-of-band callbacks related to the parent operation
|
|
45
|
+
deprecated: Declares this operation to be deprecated
|
|
46
|
+
security: Declaration of which security mechanisms can be used for this operation
|
|
47
|
+
servers: Alternative server array to service this operation
|
|
48
|
+
extensions: Specification extensions (x-* fields)
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
root_node: yaml.Node
|
|
52
|
+
tags: FieldSource[list[ValueSource[str]]] | None = fixed_field()
|
|
53
|
+
summary: FieldSource[str] | None = fixed_field()
|
|
54
|
+
description: FieldSource[str] | None = fixed_field()
|
|
55
|
+
external_docs: FieldSource["ExternalDocumentation"] | None = fixed_field(
|
|
56
|
+
metadata={"yaml_name": "externalDocs"}
|
|
57
|
+
)
|
|
58
|
+
operation_id: FieldSource[str] | None = fixed_field(metadata={"yaml_name": "operationId"})
|
|
59
|
+
parameters: FieldSource[list["Parameter | Reference"]] | None = fixed_field()
|
|
60
|
+
request_body: FieldSource[RequestBody | Reference] | None = fixed_field(
|
|
61
|
+
metadata={"yaml_name": "requestBody"}
|
|
62
|
+
)
|
|
63
|
+
responses: FieldSource[Responses] | None = fixed_field()
|
|
64
|
+
callbacks: FieldSource[dict[KeySource[str], "Callback | Reference"]] | None = fixed_field()
|
|
65
|
+
deprecated: FieldSource[bool] | None = fixed_field()
|
|
66
|
+
security: FieldSource[list["SecurityRequirement"]] | None = fixed_field()
|
|
67
|
+
servers: FieldSource[list["Server"]] | None = fixed_field()
|
|
68
|
+
extensions: dict[KeySource[str], ValueSource[YAMLValue]] = field(default_factory=dict)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def build(
|
|
72
|
+
root: yaml.Node, context: Context | None = None
|
|
73
|
+
) -> Operation | ValueSource[YAMLInvalidValue]:
|
|
74
|
+
"""
|
|
75
|
+
Build an Operation object from a YAML node.
|
|
76
|
+
|
|
77
|
+
Preserves all source data as-is, regardless of type. This is a low-level/plumbing
|
|
78
|
+
model that provides complete source fidelity for inspection and validation.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
root: The YAML node to parse (should be a MappingNode)
|
|
82
|
+
context: Optional parsing context. If None, a default context will be created.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
An Operation object if the node is valid, or a ValueSource containing
|
|
86
|
+
the invalid data if the root is not a MappingNode (preserving the invalid data
|
|
87
|
+
and its source location for validation).
|
|
88
|
+
|
|
89
|
+
Example:
|
|
90
|
+
from ruamel.yaml import YAML
|
|
91
|
+
yaml = YAML()
|
|
92
|
+
root = yaml.compose('''
|
|
93
|
+
summary: List users
|
|
94
|
+
operationId: listUsers
|
|
95
|
+
responses:
|
|
96
|
+
'200':
|
|
97
|
+
description: successful operation
|
|
98
|
+
''')
|
|
99
|
+
operation = build(root)
|
|
100
|
+
assert operation.summary.value == 'List users'
|
|
101
|
+
"""
|
|
102
|
+
context = context or Context()
|
|
103
|
+
|
|
104
|
+
# Use build_model for initial construction
|
|
105
|
+
operation = build_model(root, Operation, context=context)
|
|
106
|
+
|
|
107
|
+
# If build_model returned ValueSource (invalid node), return it immediately
|
|
108
|
+
if not isinstance(operation, Operation):
|
|
109
|
+
return operation
|
|
110
|
+
|
|
111
|
+
# Manually handle nested complex fields
|
|
112
|
+
replacements = {}
|
|
113
|
+
for key_node, value_node in root.value:
|
|
114
|
+
key = context.yaml_constructor.construct_yaml_str(key_node)
|
|
115
|
+
|
|
116
|
+
if key == "requestBody":
|
|
117
|
+
# Handle requestBody field - RequestBody or Reference
|
|
118
|
+
request_body_or_reference = build_request_body_or_reference(value_node, context)
|
|
119
|
+
replacements["request_body"] = FieldSource(
|
|
120
|
+
value=request_body_or_reference, key_node=key_node, value_node=value_node
|
|
121
|
+
)
|
|
122
|
+
elif key == "responses":
|
|
123
|
+
# Handle responses field - Responses object
|
|
124
|
+
responses_obj = build_responses(value_node, context)
|
|
125
|
+
replacements["responses"] = FieldSource(
|
|
126
|
+
value=responses_obj, key_node=key_node, value_node=value_node
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# Apply all replacements at once
|
|
130
|
+
if replacements:
|
|
131
|
+
operation = replace(operation, **replacements)
|
|
132
|
+
|
|
133
|
+
return operation
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
|
|
3
|
+
from ruamel import yaml
|
|
4
|
+
|
|
5
|
+
from ..context import Context
|
|
6
|
+
from ..fields import fixed_field
|
|
7
|
+
from ..sources import FieldSource, KeySource, ValueSource, YAMLInvalidValue, YAMLValue
|
|
8
|
+
from .builders import build_model
|
|
9
|
+
from .example import Example
|
|
10
|
+
from .media_type import MediaType
|
|
11
|
+
from .reference import Reference
|
|
12
|
+
from .reference import build as build_reference
|
|
13
|
+
from .schema import BooleanJSONSchema, Schema
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
__all__ = ["Parameter", "build", "build_parameter_or_reference"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True, slots=True)
|
|
20
|
+
class Parameter:
|
|
21
|
+
"""
|
|
22
|
+
Parameter Object representation for OpenAPI 3.1.
|
|
23
|
+
|
|
24
|
+
Describes a single operation parameter. A unique parameter is defined by a combination
|
|
25
|
+
of a name and location (in).
|
|
26
|
+
|
|
27
|
+
Attributes:
|
|
28
|
+
root_node: The top-level node representing the entire Parameter object in the original source file
|
|
29
|
+
name: The name of the parameter (case-sensitive)
|
|
30
|
+
in_: The location of the parameter. Possible values are "query", "header", "path", or "cookie"
|
|
31
|
+
description: A brief description of the parameter (may contain CommonMark syntax)
|
|
32
|
+
required: Determines whether this parameter is mandatory. For path parameters, must be true.
|
|
33
|
+
Default is false for other parameter types.
|
|
34
|
+
deprecated: Specifies that a parameter is deprecated and should be transitioned out of usage
|
|
35
|
+
allow_empty_value: Sets the ability to pass empty-valued parameters (valid only for query parameters)
|
|
36
|
+
style: Describes how the parameter value will be serialized (default depends on 'in' value)
|
|
37
|
+
explode: When true, parameter values of type array or object generate separate parameters
|
|
38
|
+
allow_reserved: Determines whether reserved characters are allowed without percent-encoding
|
|
39
|
+
schema: The schema defining the type used for the parameter
|
|
40
|
+
example: Example of the parameter's potential value
|
|
41
|
+
examples: Examples of the parameter's potential value (map of Example objects or References)
|
|
42
|
+
content: A map containing the representations for the parameter (must contain only one entry)
|
|
43
|
+
extensions: Specification extensions (x-* fields)
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
root_node: yaml.Node
|
|
47
|
+
name: FieldSource[str] | None = fixed_field()
|
|
48
|
+
in_: FieldSource[str] | None = fixed_field(metadata={"yaml_name": "in"})
|
|
49
|
+
description: FieldSource[str] | None = fixed_field()
|
|
50
|
+
required: FieldSource[bool] | None = fixed_field()
|
|
51
|
+
deprecated: FieldSource[bool] | None = fixed_field()
|
|
52
|
+
allow_empty_value: FieldSource[bool] | None = fixed_field(
|
|
53
|
+
metadata={"yaml_name": "allowEmptyValue"}
|
|
54
|
+
)
|
|
55
|
+
style: FieldSource[str] | None = fixed_field()
|
|
56
|
+
explode: FieldSource[bool] | None = fixed_field()
|
|
57
|
+
allow_reserved: FieldSource[bool] | None = fixed_field(metadata={"yaml_name": "allowReserved"})
|
|
58
|
+
schema: FieldSource["Schema | BooleanJSONSchema"] | None = fixed_field()
|
|
59
|
+
example: FieldSource[YAMLValue] | None = fixed_field()
|
|
60
|
+
examples: FieldSource[dict[KeySource[str], "Example | Reference"]] | None = fixed_field()
|
|
61
|
+
content: FieldSource[dict[KeySource[str], "MediaType"]] | None = fixed_field()
|
|
62
|
+
extensions: dict[KeySource[str], ValueSource[YAMLValue]] = field(default_factory=dict)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def build(
|
|
66
|
+
root: yaml.Node, context: Context | None = None
|
|
67
|
+
) -> Parameter | ValueSource[YAMLInvalidValue]:
|
|
68
|
+
"""
|
|
69
|
+
Build a Parameter object from a YAML node.
|
|
70
|
+
|
|
71
|
+
Preserves all source data as-is, regardless of type. This is a low-level/plumbing
|
|
72
|
+
model that provides complete source fidelity for inspection and validation.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
root: The YAML node to parse (should be a MappingNode)
|
|
76
|
+
context: Optional parsing context. If None, a default context will be created.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
A Parameter object if the node is valid, or a ValueSource containing
|
|
80
|
+
the invalid data if the root is not a MappingNode (preserving the invalid data
|
|
81
|
+
and its source location for validation).
|
|
82
|
+
|
|
83
|
+
Example:
|
|
84
|
+
from ruamel.yaml import YAML
|
|
85
|
+
yaml = YAML()
|
|
86
|
+
root = yaml.compose('''
|
|
87
|
+
name: userId
|
|
88
|
+
in: path
|
|
89
|
+
required: true
|
|
90
|
+
schema:
|
|
91
|
+
type: integer
|
|
92
|
+
''')
|
|
93
|
+
parameter = build(root)
|
|
94
|
+
assert parameter.name.value == 'userId'
|
|
95
|
+
"""
|
|
96
|
+
return build_model(root, Parameter, context=context)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def build_parameter_or_reference(
|
|
100
|
+
node: yaml.Node, context: Context
|
|
101
|
+
) -> Parameter | Reference | ValueSource[YAMLInvalidValue]:
|
|
102
|
+
"""
|
|
103
|
+
Build either a Parameter or Reference from a YAML node.
|
|
104
|
+
|
|
105
|
+
This helper handles the polymorphic nature of OpenAPI where many fields
|
|
106
|
+
can contain either a Parameter object or a Reference object ($ref).
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
node: The YAML node to parse
|
|
110
|
+
context: Parsing context
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
A Parameter, Reference, or ValueSource if the node is invalid
|
|
114
|
+
"""
|
|
115
|
+
# Check if it's a reference (has $ref key)
|
|
116
|
+
if isinstance(node, yaml.MappingNode):
|
|
117
|
+
for key_node, _ in node.value:
|
|
118
|
+
key = context.yaml_constructor.construct_yaml_str(key_node)
|
|
119
|
+
if key == "$ref":
|
|
120
|
+
return build_reference(node, context)
|
|
121
|
+
|
|
122
|
+
# Otherwise, try to build as Parameter
|
|
123
|
+
return build(node, context)
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
from dataclasses import dataclass, field, replace
|
|
2
|
+
|
|
3
|
+
from ruamel import yaml
|
|
4
|
+
|
|
5
|
+
from ..context import Context
|
|
6
|
+
from ..fields import fixed_field
|
|
7
|
+
from ..sources import FieldSource, KeySource, ValueSource, YAMLInvalidValue, YAMLValue
|
|
8
|
+
from .builders import build_model
|
|
9
|
+
from .operation import Operation
|
|
10
|
+
from .operation import build as build_operation
|
|
11
|
+
from .parameter import Parameter
|
|
12
|
+
from .reference import Reference
|
|
13
|
+
from .server import Server
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
__all__ = ["PathItem", "build"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True, slots=True)
|
|
20
|
+
class PathItem:
|
|
21
|
+
"""
|
|
22
|
+
Path Item Object representation for OpenAPI 3.1.
|
|
23
|
+
|
|
24
|
+
Describes the operations available on a single path. A Path Item may be empty,
|
|
25
|
+
due to ACL constraints.
|
|
26
|
+
|
|
27
|
+
Attributes:
|
|
28
|
+
root_node: The top-level node representing the entire Path Item object in the original source file
|
|
29
|
+
ref: Allows referencing an external definition of this path item
|
|
30
|
+
summary: Optional string summary intended to apply to all operations in this path
|
|
31
|
+
description: Optional string description (may contain CommonMark syntax)
|
|
32
|
+
get: Definition of a GET operation on this path
|
|
33
|
+
put: Definition of a PUT operation on this path
|
|
34
|
+
post: Definition of a POST operation on this path
|
|
35
|
+
delete: Definition of a DELETE operation on this path
|
|
36
|
+
options: Definition of an OPTIONS operation on this path
|
|
37
|
+
head: Definition of a HEAD operation on this path
|
|
38
|
+
patch: Definition of a PATCH operation on this path
|
|
39
|
+
trace: Definition of a TRACE operation on this path
|
|
40
|
+
servers: Alternative server array to service all operations in this path
|
|
41
|
+
parameters: List of parameters that are applicable for all operations in this path
|
|
42
|
+
extensions: Specification extensions (x-* fields)
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
root_node: yaml.Node
|
|
46
|
+
ref: FieldSource[str] | None = fixed_field(metadata={"yaml_name": "$ref"})
|
|
47
|
+
summary: FieldSource[str] | None = fixed_field()
|
|
48
|
+
description: FieldSource[str] | None = fixed_field()
|
|
49
|
+
get: FieldSource[Operation] | None = fixed_field()
|
|
50
|
+
put: FieldSource[Operation] | None = fixed_field()
|
|
51
|
+
post: FieldSource[Operation] | None = fixed_field()
|
|
52
|
+
delete: FieldSource[Operation] | None = fixed_field()
|
|
53
|
+
options: FieldSource[Operation] | None = fixed_field()
|
|
54
|
+
head: FieldSource[Operation] | None = fixed_field()
|
|
55
|
+
patch: FieldSource[Operation] | None = fixed_field()
|
|
56
|
+
trace: FieldSource[Operation] | None = fixed_field()
|
|
57
|
+
servers: FieldSource[list["Server"]] | None = fixed_field()
|
|
58
|
+
parameters: FieldSource[list["Parameter | Reference"]] | None = fixed_field()
|
|
59
|
+
extensions: dict[KeySource[str], ValueSource[YAMLValue]] = field(default_factory=dict)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def build(
|
|
63
|
+
root: yaml.Node, context: Context | None = None
|
|
64
|
+
) -> PathItem | ValueSource[YAMLInvalidValue]:
|
|
65
|
+
"""
|
|
66
|
+
Build a PathItem object from a YAML node.
|
|
67
|
+
|
|
68
|
+
Preserves all source data as-is, regardless of type. This is a low-level/plumbing
|
|
69
|
+
model that provides complete source fidelity for inspection and validation.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
root: The YAML node to parse (should be a MappingNode)
|
|
73
|
+
context: Optional parsing context. If None, a default context will be created.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
A PathItem object if the node is valid, or a ValueSource containing
|
|
77
|
+
the invalid data if the root is not a MappingNode (preserving the invalid data
|
|
78
|
+
and its source location for validation).
|
|
79
|
+
|
|
80
|
+
Example:
|
|
81
|
+
from ruamel.yaml import YAML
|
|
82
|
+
yaml = YAML()
|
|
83
|
+
root = yaml.compose('''
|
|
84
|
+
summary: User operations
|
|
85
|
+
get:
|
|
86
|
+
summary: List users
|
|
87
|
+
responses:
|
|
88
|
+
'200':
|
|
89
|
+
description: successful operation
|
|
90
|
+
post:
|
|
91
|
+
summary: Create user
|
|
92
|
+
responses:
|
|
93
|
+
'201':
|
|
94
|
+
description: user created
|
|
95
|
+
''')
|
|
96
|
+
path_item = build(root)
|
|
97
|
+
assert path_item.get is not None
|
|
98
|
+
assert path_item.post is not None
|
|
99
|
+
"""
|
|
100
|
+
context = context or Context()
|
|
101
|
+
|
|
102
|
+
# Use build_model for initial construction
|
|
103
|
+
path_item = build_model(root, PathItem, context=context)
|
|
104
|
+
|
|
105
|
+
# If build_model returned ValueSource (invalid node), return it immediately
|
|
106
|
+
if not isinstance(path_item, PathItem):
|
|
107
|
+
return path_item
|
|
108
|
+
|
|
109
|
+
# Manually handle nested complex fields
|
|
110
|
+
replacements = {}
|
|
111
|
+
for key_node, value_node in root.value:
|
|
112
|
+
key = context.yaml_constructor.construct_yaml_str(key_node)
|
|
113
|
+
|
|
114
|
+
# Handle HTTP method operation fields
|
|
115
|
+
if key in ("get", "put", "post", "delete", "options", "head", "patch", "trace"):
|
|
116
|
+
operation_obj = build_operation(value_node, context)
|
|
117
|
+
replacements[key] = FieldSource(
|
|
118
|
+
value=operation_obj, key_node=key_node, value_node=value_node
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# Apply all replacements at once
|
|
122
|
+
if replacements:
|
|
123
|
+
path_item = replace(path_item, **replacements)
|
|
124
|
+
|
|
125
|
+
return path_item
|