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.
Files changed (73) hide show
  1. jentic/apitools/openapi/datamodels/low/extractors.py +3 -3
  2. jentic/apitools/openapi/datamodels/low/v30/__init__.py +76 -0
  3. jentic/apitools/openapi/datamodels/low/v30/builders/__init__.py +312 -0
  4. jentic/apitools/openapi/datamodels/low/v30/callback.py +131 -0
  5. jentic/apitools/openapi/datamodels/low/v30/components.py +236 -0
  6. jentic/apitools/openapi/datamodels/low/v30/contact.py +4 -10
  7. jentic/apitools/openapi/datamodels/low/v30/discriminator.py +4 -9
  8. jentic/apitools/openapi/datamodels/low/v30/encoding.py +81 -0
  9. jentic/apitools/openapi/datamodels/low/v30/example.py +91 -0
  10. jentic/apitools/openapi/datamodels/low/v30/external_documentation.py +4 -10
  11. jentic/apitools/openapi/datamodels/low/v30/header.py +120 -0
  12. jentic/apitools/openapi/datamodels/low/v30/info.py +14 -23
  13. jentic/apitools/openapi/datamodels/low/v30/license.py +4 -10
  14. jentic/apitools/openapi/datamodels/low/v30/link.py +141 -0
  15. jentic/apitools/openapi/datamodels/low/v30/media_type.py +110 -0
  16. jentic/apitools/openapi/datamodels/low/v30/oauth_flow.py +4 -10
  17. jentic/apitools/openapi/datamodels/low/v30/oauth_flows.py +7 -15
  18. jentic/apitools/openapi/datamodels/low/v30/openapi.py +149 -0
  19. jentic/apitools/openapi/datamodels/low/v30/operation.py +134 -0
  20. jentic/apitools/openapi/datamodels/low/v30/parameter.py +123 -0
  21. jentic/apitools/openapi/datamodels/low/v30/path_item.py +125 -0
  22. jentic/apitools/openapi/datamodels/low/v30/paths.py +108 -0
  23. jentic/apitools/openapi/datamodels/low/v30/reference.py +5 -9
  24. jentic/apitools/openapi/datamodels/low/v30/request_body.py +108 -0
  25. jentic/apitools/openapi/datamodels/low/v30/response.py +104 -0
  26. jentic/apitools/openapi/datamodels/low/v30/responses.py +109 -0
  27. jentic/apitools/openapi/datamodels/low/v30/schema.py +81 -97
  28. jentic/apitools/openapi/datamodels/low/v30/security_requirement.py +14 -9
  29. jentic/apitools/openapi/datamodels/low/v30/security_scheme.py +42 -22
  30. jentic/apitools/openapi/datamodels/low/v30/server.py +111 -0
  31. jentic/apitools/openapi/datamodels/low/v30/server_variable.py +4 -10
  32. jentic/apitools/openapi/datamodels/low/v30/tag.py +8 -46
  33. jentic/apitools/openapi/datamodels/low/v30/xml.py +4 -10
  34. jentic/apitools/openapi/datamodels/low/v31/__init__.py +77 -0
  35. jentic/apitools/openapi/datamodels/low/v31/builders/__init__.py +347 -0
  36. jentic/apitools/openapi/datamodels/low/v31/callback.py +131 -0
  37. jentic/apitools/openapi/datamodels/low/v31/components.py +240 -0
  38. jentic/apitools/openapi/datamodels/low/v31/contact.py +61 -0
  39. jentic/apitools/openapi/datamodels/low/v31/discriminator.py +62 -0
  40. jentic/apitools/openapi/datamodels/low/v31/encoding.py +81 -0
  41. jentic/apitools/openapi/datamodels/low/v31/example.py +91 -0
  42. jentic/apitools/openapi/datamodels/low/v31/external_documentation.py +59 -0
  43. jentic/apitools/openapi/datamodels/low/v31/header.py +120 -0
  44. jentic/apitools/openapi/datamodels/low/v31/info.py +116 -0
  45. jentic/apitools/openapi/datamodels/low/v31/license.py +61 -0
  46. jentic/apitools/openapi/datamodels/low/v31/link.py +141 -0
  47. jentic/apitools/openapi/datamodels/low/v31/media_type.py +110 -0
  48. jentic/apitools/openapi/datamodels/low/v31/oauth_flow.py +65 -0
  49. jentic/apitools/openapi/datamodels/low/v31/oauth_flows.py +108 -0
  50. jentic/apitools/openapi/datamodels/low/v31/openapi.py +168 -0
  51. jentic/apitools/openapi/datamodels/low/v31/operation.py +133 -0
  52. jentic/apitools/openapi/datamodels/low/v31/parameter.py +123 -0
  53. jentic/apitools/openapi/datamodels/low/v31/path_item.py +125 -0
  54. jentic/apitools/openapi/datamodels/low/v31/paths.py +108 -0
  55. jentic/apitools/openapi/datamodels/low/v31/reference.py +65 -0
  56. jentic/apitools/openapi/datamodels/low/v31/request_body.py +108 -0
  57. jentic/apitools/openapi/datamodels/low/v31/response.py +104 -0
  58. jentic/apitools/openapi/datamodels/low/v31/responses.py +109 -0
  59. jentic/apitools/openapi/datamodels/low/v31/schema.py +498 -0
  60. jentic/apitools/openapi/datamodels/low/v31/security_requirement.py +106 -0
  61. jentic/apitools/openapi/datamodels/low/v31/security_scheme.py +129 -0
  62. jentic/apitools/openapi/datamodels/low/v31/server.py +111 -0
  63. jentic/apitools/openapi/datamodels/low/v31/server_variable.py +70 -0
  64. jentic/apitools/openapi/datamodels/low/v31/tag.py +63 -0
  65. jentic/apitools/openapi/datamodels/low/v31/xml.py +54 -0
  66. jentic_openapi_datamodels-1.0.0a20.dist-info/METADATA +379 -0
  67. jentic_openapi_datamodels-1.0.0a20.dist-info/RECORD +75 -0
  68. jentic/apitools/openapi/datamodels/low/model_builder.py +0 -129
  69. jentic_openapi_datamodels-1.0.0a18.dist-info/METADATA +0 -211
  70. jentic_openapi_datamodels-1.0.0a18.dist-info/RECORD +0 -27
  71. {jentic_openapi_datamodels-1.0.0a18.dist-info → jentic_openapi_datamodels-1.0.0a20.dist-info}/WHEEL +0 -0
  72. {jentic_openapi_datamodels-1.0.0a18.dist-info → jentic_openapi_datamodels-1.0.0a20.dist-info}/licenses/LICENSE +0 -0
  73. {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