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,236 @@
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 .callback import Callback
10
+ from .example import Example, build_example_or_reference
11
+ from .header import Header
12
+ from .link import Link
13
+ from .parameter import Parameter, build_parameter_or_reference
14
+ from .reference import Reference
15
+ from .request_body import RequestBody, build_request_body_or_reference
16
+ from .response import Response, build_response_or_reference
17
+ from .schema import Schema, build_schema_or_reference
18
+ from .security_scheme import SecurityScheme, build_security_scheme_or_reference
19
+
20
+
21
+ __all__ = ["Components", "build"]
22
+
23
+
24
+ @dataclass(frozen=True, slots=True)
25
+ class Components:
26
+ r"""
27
+ Components Object representation for OpenAPI 3.0.
28
+
29
+ Holds reusable objects for different aspects of the OAS. All objects defined within
30
+ the components object have no effect on the API unless they are explicitly referenced
31
+ from properties outside the components object.
32
+
33
+ All component keys MUST match the regex pattern: ^[a-zA-Z0-9\.\-_]+$
34
+ (Note: This is a validation concern and not enforced by this low-level model)
35
+
36
+ Attributes:
37
+ root_node: The top-level node representing the entire Components object in the original source file
38
+ schemas: Reusable Schema Objects or Reference Objects
39
+ responses: Reusable Response Objects or Reference Objects
40
+ parameters: Reusable Parameter Objects or Reference Objects
41
+ examples: Reusable Example Objects or Reference Objects
42
+ request_bodies: Reusable Request Body Objects or Reference Objects
43
+ headers: Reusable Header Objects or Reference Objects
44
+ security_schemes: Reusable Security Scheme Objects or Reference Objects
45
+ links: Reusable Link Objects or Reference Objects
46
+ callbacks: Reusable Callback Objects or Reference Objects
47
+ extensions: Specification extensions (x-* fields)
48
+ """
49
+
50
+ root_node: yaml.Node
51
+ schemas: FieldSource[dict[KeySource[str], Schema | Reference]] | None = fixed_field()
52
+ responses: FieldSource[dict[KeySource[str], Response | Reference]] | None = fixed_field()
53
+ parameters: FieldSource[dict[KeySource[str], Parameter | Reference]] | None = fixed_field()
54
+ examples: FieldSource[dict[KeySource[str], Example | Reference]] | None = fixed_field()
55
+ request_bodies: FieldSource[dict[KeySource[str], RequestBody | Reference]] | None = fixed_field(
56
+ metadata={"yaml_name": "requestBodies"}
57
+ )
58
+ headers: FieldSource[dict[KeySource[str], "Header | Reference"]] | None = fixed_field()
59
+ security_schemes: FieldSource[dict[KeySource[str], SecurityScheme | Reference]] | None = (
60
+ fixed_field(metadata={"yaml_name": "securitySchemes"})
61
+ )
62
+ links: FieldSource[dict[KeySource[str], "Link | Reference"]] | None = fixed_field()
63
+ callbacks: FieldSource[dict[KeySource[str], "Callback | Reference"]] | None = fixed_field()
64
+ extensions: dict[KeySource[str], ValueSource[YAMLValue]] = field(default_factory=dict)
65
+
66
+
67
+ def build(
68
+ root: yaml.Node, context: Context | None = None
69
+ ) -> Components | ValueSource[YAMLInvalidValue]:
70
+ """
71
+ Build a Components object from a YAML node.
72
+
73
+ Preserves all source data as-is, regardless of type. This is a low-level/plumbing
74
+ model that provides complete source fidelity for inspection and validation.
75
+
76
+ Args:
77
+ root: The YAML node to parse (should be a MappingNode)
78
+ context: Optional parsing context. If None, a default context will be created.
79
+
80
+ Returns:
81
+ A Components object if the node is valid, or a ValueSource containing
82
+ the invalid data if the root is not a MappingNode (preserving the invalid data
83
+ and its source location for validation).
84
+
85
+ Example:
86
+ from ruamel.yaml import YAML
87
+ yaml = YAML()
88
+ root = yaml.compose('''
89
+ schemas:
90
+ User:
91
+ type: object
92
+ properties:
93
+ id:
94
+ type: integer
95
+ name:
96
+ type: string
97
+ responses:
98
+ NotFound:
99
+ description: Entity not found
100
+ ''')
101
+ components = build(root)
102
+ assert 'User' in {k.value for k in components.schemas.value.keys()}
103
+ """
104
+ context = context or Context()
105
+
106
+ # Use build_model for initial construction
107
+ components_obj = build_model(root, Components, context=context)
108
+
109
+ # If build_model returned ValueSource (invalid node), return it immediately
110
+ if not isinstance(components_obj, Components):
111
+ return components_obj
112
+
113
+ # Manually handle nested complex fields that aren't covered by build_model
114
+ replacements = {}
115
+ for key_node, value_node in root.value:
116
+ key = context.yaml_constructor.construct_yaml_str(key_node)
117
+
118
+ if key == "schemas":
119
+ # Handle schemas field - map of Schema or Reference objects
120
+ if isinstance(value_node, yaml.MappingNode):
121
+ schemas_dict = {}
122
+ for schema_key_node, schema_value_node in value_node.value:
123
+ schema_key = context.yaml_constructor.construct_yaml_str(schema_key_node)
124
+ schema_or_reference = build_schema_or_reference(schema_value_node, context)
125
+ schemas_dict[KeySource(value=schema_key, key_node=schema_key_node)] = (
126
+ schema_or_reference
127
+ )
128
+ replacements["schemas"] = FieldSource(
129
+ value=schemas_dict, key_node=key_node, value_node=value_node
130
+ )
131
+ else:
132
+ # Not a mapping - preserve as-is for validation
133
+ replacements["schemas"] = build_field_source(key_node, value_node, context)
134
+
135
+ elif key == "responses":
136
+ # Handle responses field - map of Response or Reference objects
137
+ if isinstance(value_node, yaml.MappingNode):
138
+ responses_dict = {}
139
+ for response_key_node, response_value_node in value_node.value:
140
+ response_key = context.yaml_constructor.construct_yaml_str(response_key_node)
141
+ response_or_reference = build_response_or_reference(
142
+ response_value_node, context
143
+ )
144
+ responses_dict[KeySource(value=response_key, key_node=response_key_node)] = (
145
+ response_or_reference
146
+ )
147
+ replacements["responses"] = FieldSource(
148
+ value=responses_dict, key_node=key_node, value_node=value_node
149
+ )
150
+ else:
151
+ # Not a mapping - preserve as-is for validation
152
+ replacements["responses"] = build_field_source(key_node, value_node, context)
153
+
154
+ elif key == "parameters":
155
+ # Handle parameters field - map of Parameter or Reference objects
156
+ if isinstance(value_node, yaml.MappingNode):
157
+ parameters_dict = {}
158
+ for parameter_key_node, parameter_value_node in value_node.value:
159
+ parameter_key = context.yaml_constructor.construct_yaml_str(parameter_key_node)
160
+ parameter_or_reference = build_parameter_or_reference(
161
+ parameter_value_node, context
162
+ )
163
+ parameters_dict[KeySource(value=parameter_key, key_node=parameter_key_node)] = (
164
+ parameter_or_reference
165
+ )
166
+ replacements["parameters"] = FieldSource(
167
+ value=parameters_dict, key_node=key_node, value_node=value_node
168
+ )
169
+ else:
170
+ # Not a mapping - preserve as-is for validation
171
+ replacements["parameters"] = build_field_source(key_node, value_node, context)
172
+
173
+ elif key == "examples":
174
+ # Handle examples field - map of Example or Reference objects
175
+ if isinstance(value_node, yaml.MappingNode):
176
+ examples_dict = {}
177
+ for example_key_node, example_value_node in value_node.value:
178
+ example_key = context.yaml_constructor.construct_yaml_str(example_key_node)
179
+ example_or_reference = build_example_or_reference(example_value_node, context)
180
+ examples_dict[KeySource(value=example_key, key_node=example_key_node)] = (
181
+ example_or_reference
182
+ )
183
+ replacements["examples"] = FieldSource(
184
+ value=examples_dict, key_node=key_node, value_node=value_node
185
+ )
186
+ else:
187
+ # Not a mapping - preserve as-is for validation
188
+ replacements["examples"] = build_field_source(key_node, value_node, context)
189
+
190
+ elif key == "requestBodies":
191
+ # Handle requestBodies field - map of RequestBody or Reference objects
192
+ if isinstance(value_node, yaml.MappingNode):
193
+ request_bodies_dict = {}
194
+ for request_body_key_node, request_body_value_node in value_node.value:
195
+ request_body_key = context.yaml_constructor.construct_yaml_str(
196
+ request_body_key_node
197
+ )
198
+ request_body_or_reference = build_request_body_or_reference(
199
+ request_body_value_node, context
200
+ )
201
+ request_bodies_dict[
202
+ KeySource(value=request_body_key, key_node=request_body_key_node)
203
+ ] = request_body_or_reference
204
+ replacements["request_bodies"] = FieldSource(
205
+ value=request_bodies_dict, key_node=key_node, value_node=value_node
206
+ )
207
+ else:
208
+ # Not a mapping - preserve as-is for validation
209
+ replacements["request_bodies"] = build_field_source(key_node, value_node, context)
210
+
211
+ elif key == "securitySchemes":
212
+ # Handle securitySchemes field - map of SecurityScheme or Reference objects
213
+ if isinstance(value_node, yaml.MappingNode):
214
+ security_schemes_dict = {}
215
+ for security_scheme_key_node, security_scheme_value_node in value_node.value:
216
+ security_scheme_key = context.yaml_constructor.construct_yaml_str(
217
+ security_scheme_key_node
218
+ )
219
+ security_scheme_or_reference = build_security_scheme_or_reference(
220
+ security_scheme_value_node, context
221
+ )
222
+ security_schemes_dict[
223
+ KeySource(value=security_scheme_key, key_node=security_scheme_key_node)
224
+ ] = security_scheme_or_reference
225
+ replacements["security_schemes"] = FieldSource(
226
+ value=security_schemes_dict, key_node=key_node, value_node=value_node
227
+ )
228
+ else:
229
+ # Not a mapping - preserve as-is for validation
230
+ replacements["security_schemes"] = build_field_source(key_node, value_node, context)
231
+
232
+ # Apply all replacements at once
233
+ if replacements:
234
+ components_obj = replace(components_obj, **replacements)
235
+
236
+ return components_obj
@@ -2,16 +2,10 @@ from dataclasses import dataclass, field
2
2
 
3
3
  from ruamel import yaml
4
4
 
5
- from jentic.apitools.openapi.datamodels.low.context import Context
6
- from jentic.apitools.openapi.datamodels.low.fields import fixed_field
7
- from jentic.apitools.openapi.datamodels.low.model_builder import build_model
8
- from jentic.apitools.openapi.datamodels.low.sources import (
9
- FieldSource,
10
- KeySource,
11
- ValueSource,
12
- YAMLInvalidValue,
13
- YAMLValue,
14
- )
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
15
9
 
16
10
 
17
11
  __all__ = ["Contact", "build"]
@@ -2,15 +2,10 @@ from dataclasses import dataclass
2
2
 
3
3
  from ruamel import yaml
4
4
 
5
- from jentic.apitools.openapi.datamodels.low.context import Context
6
- from jentic.apitools.openapi.datamodels.low.fields import fixed_field
7
- from jentic.apitools.openapi.datamodels.low.model_builder import build_model
8
- from jentic.apitools.openapi.datamodels.low.sources import (
9
- FieldSource,
10
- KeySource,
11
- ValueSource,
12
- YAMLInvalidValue,
13
- )
5
+ from ..context import Context
6
+ from ..fields import fixed_field
7
+ from ..sources import FieldSource, KeySource, ValueSource, YAMLInvalidValue
8
+ from .builders import build_model
14
9
 
15
10
 
16
11
  __all__ = ["Discriminator", "build"]
@@ -0,0 +1,81 @@
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 .header import Header
10
+ from .reference import Reference
11
+
12
+
13
+ __all__ = ["Encoding", "build"]
14
+
15
+
16
+ @dataclass(frozen=True, slots=True)
17
+ class Encoding:
18
+ """
19
+ Encoding Object representation for OpenAPI 3.0.
20
+
21
+ A single encoding definition applied to a single schema property.
22
+
23
+ Attributes:
24
+ root_node: The top-level node representing the entire Encoding object in the original source file
25
+ contentType: The Content-Type for encoding a specific property. Default value depends on the property type:
26
+ for string with format being binary – application/octet-stream;
27
+ for other primitive types – text/plain;
28
+ for object - application/json;
29
+ for array – the default is defined based on the inner type.
30
+ headers: A map allowing additional information to be provided as headers. Content-Type is described
31
+ separately and SHALL be ignored if included.
32
+ style: Describes how a specific property value will be serialized depending on its type.
33
+ explode: When this is true, property values of type array or object generate separate parameters
34
+ for each value of the array, or key-value-pair of the map. For other data types this property
35
+ has no effect. Default value is true.
36
+ allowReserved: Determines whether the parameter value SHOULD allow reserved characters, as defined by
37
+ RFC3986 :/?#[]@!$&'()*+,;= to be included without percent-encoding. The default value is false.
38
+ extensions: Specification extensions (x-* fields)
39
+ """
40
+
41
+ root_node: yaml.Node
42
+ contentType: FieldSource[str] | None = fixed_field()
43
+ headers: FieldSource[dict[KeySource[str], "Header | Reference"]] | None = fixed_field()
44
+ style: FieldSource[str] | None = fixed_field()
45
+ explode: FieldSource[bool] | None = fixed_field()
46
+ allowReserved: FieldSource[bool] | None = fixed_field()
47
+ extensions: dict[KeySource[str], ValueSource[YAMLValue]] = field(default_factory=dict)
48
+
49
+
50
+ def build(
51
+ root: yaml.Node, context: Context | None = None
52
+ ) -> Encoding | ValueSource[YAMLInvalidValue]:
53
+ """
54
+ Build an Encoding object from a YAML node.
55
+
56
+ Preserves all source data as-is, regardless of type. This is a low-level/plumbing
57
+ model that provides complete source fidelity for inspection and validation.
58
+
59
+ Args:
60
+ root: The YAML node to parse (should be a MappingNode)
61
+ context: Optional parsing context. If None, a default context will be created.
62
+
63
+ Returns:
64
+ An Encoding object if the node is valid, or a ValueSource containing
65
+ the invalid data if the root is not a MappingNode (preserving the invalid data
66
+ and its source location for validation).
67
+
68
+ Example:
69
+ from ruamel.yaml import YAML
70
+ yaml = YAML()
71
+ root = yaml.compose('''
72
+ contentType: application/xml
73
+ headers:
74
+ X-Rate-Limit:
75
+ schema:
76
+ type: integer
77
+ ''')
78
+ encoding = build(root)
79
+ assert encoding.contentType.value == 'application/xml'
80
+ """
81
+ return build_model(root, Encoding, context=context)
@@ -0,0 +1,91 @@
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 .reference import Reference
10
+ from .reference import build as build_reference
11
+
12
+
13
+ __all__ = ["Example", "build", "build_example_or_reference"]
14
+
15
+
16
+ @dataclass(frozen=True, slots=True)
17
+ class Example:
18
+ """
19
+ Example Object representation for OpenAPI 3.0.
20
+
21
+ Attributes:
22
+ root_node: The top-level node representing the entire Example object in the original source file
23
+ summary: A short description of the example.
24
+ description: A long description for the example. CommonMark syntax MAY be used for rich text representation.
25
+ value: Embedded literal example. The value field and externalValue field are mutually exclusive.
26
+ external_value: A URL that points to the literal example. This provides the capability to reference
27
+ examples that cannot be inlined. The value field and externalValue field are mutually exclusive.
28
+ extensions: Specification extensions (x-* fields)
29
+ """
30
+
31
+ root_node: yaml.Node
32
+ summary: FieldSource[str] | None = fixed_field()
33
+ description: FieldSource[str] | None = fixed_field()
34
+ value: FieldSource[YAMLValue] | None = fixed_field()
35
+ external_value: FieldSource[str] | None = fixed_field(metadata={"yaml_name": "externalValue"})
36
+ extensions: dict[KeySource[str], ValueSource[YAMLValue]] = field(default_factory=dict)
37
+
38
+
39
+ def build(
40
+ root: yaml.Node, context: Context | None = None
41
+ ) -> Example | ValueSource[YAMLInvalidValue]:
42
+ """
43
+ Build an Example object from a YAML node.
44
+
45
+ Preserves all source data as-is, regardless of type. This is a low-level/plumbing
46
+ model that provides complete source fidelity for inspection and validation.
47
+
48
+ Args:
49
+ root: The YAML node to parse (should be a MappingNode)
50
+ context: Optional parsing context. If None, a default context will be created.
51
+
52
+ Returns:
53
+ An Example object if the node is valid, or a ValueSource containing
54
+ the invalid data if the root is not a MappingNode (preserving the invalid data
55
+ and its source location for validation).
56
+
57
+ Example:
58
+ from ruamel.yaml import YAML
59
+ yaml = YAML()
60
+ root = yaml.compose("summary: A user example\\nvalue:\\n id: 1\\n name: John")
61
+ example = build(root)
62
+ assert example.summary.value == 'A user example'
63
+ """
64
+ return build_model(root, Example, context=context)
65
+
66
+
67
+ def build_example_or_reference(
68
+ node: yaml.Node, context: Context
69
+ ) -> Example | Reference | ValueSource[YAMLInvalidValue]:
70
+ """
71
+ Build either an Example or Reference from a YAML node.
72
+
73
+ This helper handles the polymorphic nature of OpenAPI where many fields
74
+ can contain either an Example object or a Reference object ($ref).
75
+
76
+ Args:
77
+ node: The YAML node to parse
78
+ context: Parsing context
79
+
80
+ Returns:
81
+ An Example, Reference, or ValueSource if the node is invalid
82
+ """
83
+ # Check if it's a reference (has $ref key)
84
+ if isinstance(node, yaml.MappingNode):
85
+ for key_node, _ in node.value:
86
+ key = context.yaml_constructor.construct_yaml_str(key_node)
87
+ if key == "$ref":
88
+ return build_reference(node, context)
89
+
90
+ # Otherwise, try to build as Example
91
+ return build(node, context)
@@ -2,16 +2,10 @@ from dataclasses import dataclass, field
2
2
 
3
3
  from ruamel import yaml
4
4
 
5
- from jentic.apitools.openapi.datamodels.low.context import Context
6
- from jentic.apitools.openapi.datamodels.low.fields import fixed_field
7
- from jentic.apitools.openapi.datamodels.low.model_builder import build_model
8
- from jentic.apitools.openapi.datamodels.low.sources import (
9
- FieldSource,
10
- KeySource,
11
- ValueSource,
12
- YAMLInvalidValue,
13
- YAMLValue,
14
- )
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
15
9
 
16
10
 
17
11
  __all__ = ["ExternalDocumentation", "build"]
@@ -0,0 +1,120 @@
1
+ from dataclasses import dataclass, field
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 .example import Example
11
+ from .reference import Reference
12
+ from .reference import build as build_reference
13
+ from .schema import Schema
14
+
15
+
16
+ if TYPE_CHECKING:
17
+ from .media_type import MediaType
18
+
19
+
20
+ __all__ = ["Header", "build", "build_header_or_reference"]
21
+
22
+
23
+ @dataclass(frozen=True, slots=True)
24
+ class Header:
25
+ """
26
+ Header Object representation for OpenAPI 3.0.
27
+
28
+ The Header Object follows the structure of the Parameter Object, including determining its
29
+ serialization strategy based on whether schema or content is present, with the following changes:
30
+ - name MUST NOT be specified, it is given in the corresponding headers map.
31
+ - in MUST NOT be specified, it is implicitly in header.
32
+ - All traits that are affected by the location MUST be applicable to a location of header
33
+ (for example, style). This means that allowEmptyValue and allowReserved MUST NOT be used,
34
+ and style, if used, MUST be limited to "simple".
35
+
36
+ Attributes:
37
+ root_node: The top-level node representing the entire Header object in the original source file
38
+ description: A brief description of the header. CommonMark syntax MAY be used for rich text representation.
39
+ required: Determines whether this header is mandatory. Default value is false.
40
+ deprecated: Specifies that a header is deprecated and SHOULD be transitioned out of usage. Default value is false.
41
+ style: Describes how the header value will be serialized depending on the type of the header value.
42
+ If used, MUST be limited to "simple".
43
+ explode: When this is true, header values of type array or object generate separate parameters for each value of the array or key-value pair of the map. Default value is false.
44
+ schema: The schema defining the type used for the header.
45
+ example: Example of the header's potential value. The example SHOULD match the specified schema and encoding properties if present.
46
+ examples: Examples of the header's potential value. Each example SHOULD contain a value in the correct format as specified in the header encoding.
47
+ content: A map containing the representations for the header. The key is the media type and the value describes it.
48
+ extensions: Specification extensions (x-* fields)
49
+ """
50
+
51
+ root_node: yaml.Node
52
+ description: FieldSource[str] | None = fixed_field()
53
+ required: FieldSource[bool] | None = fixed_field()
54
+ deprecated: FieldSource[bool] | None = fixed_field()
55
+ style: FieldSource[str] | None = fixed_field()
56
+ explode: FieldSource[bool] | None = fixed_field()
57
+ schema: FieldSource["Schema | Reference"] | None = fixed_field()
58
+ example: FieldSource[YAMLValue] | None = fixed_field()
59
+ examples: FieldSource[dict[KeySource[str], "Example | Reference"]] | None = fixed_field()
60
+ content: FieldSource[dict[KeySource[str], "MediaType"]] | None = fixed_field()
61
+ extensions: dict[KeySource[str], ValueSource[YAMLValue]] = field(default_factory=dict)
62
+
63
+
64
+ def build(
65
+ root: yaml.Node, context: Context | None = None
66
+ ) -> Header | ValueSource[YAMLInvalidValue]:
67
+ """
68
+ Build a Header object from a YAML node.
69
+
70
+ Preserves all source data as-is, regardless of type. This is a low-level/plumbing
71
+ model that provides complete source fidelity for inspection and validation.
72
+
73
+ Args:
74
+ root: The YAML node to parse (should be a MappingNode)
75
+ context: Optional parsing context. If None, a default context will be created.
76
+
77
+ Returns:
78
+ A Header object if the node is valid, or a ValueSource containing
79
+ the invalid data if the root is not a MappingNode (preserving the invalid data
80
+ and its source location for validation).
81
+
82
+ Example:
83
+ from ruamel.yaml import YAML
84
+ yaml = YAML()
85
+ root = yaml.compose('''
86
+ description: The number of allowed requests in the current period
87
+ schema:
88
+ type: integer
89
+ ''')
90
+ header = build(root)
91
+ assert header.description.value == 'The number of allowed requests in the current period'
92
+ """
93
+ return build_model(root, Header, context=context)
94
+
95
+
96
+ def build_header_or_reference(
97
+ node: yaml.Node, context: Context
98
+ ) -> Header | Reference | ValueSource[YAMLInvalidValue]:
99
+ """
100
+ Build either a Header or Reference from a YAML node.
101
+
102
+ This helper handles the polymorphic nature of OpenAPI where many fields
103
+ can contain either a Header object or a Reference object ($ref).
104
+
105
+ Args:
106
+ node: The YAML node to parse
107
+ context: Parsing context
108
+
109
+ Returns:
110
+ A Header, Reference, or ValueSource if the node is invalid
111
+ """
112
+ # Check if it's a reference (has $ref key)
113
+ if isinstance(node, yaml.MappingNode):
114
+ for key_node, _ in node.value:
115
+ key = context.yaml_constructor.construct_yaml_str(key_node)
116
+ if key == "$ref":
117
+ return build_reference(node, context)
118
+
119
+ # Otherwise, try to build as Header
120
+ return build(node, context)
@@ -2,20 +2,14 @@ from dataclasses import dataclass, field, replace
2
2
 
3
3
  from ruamel import yaml
4
4
 
5
- from jentic.apitools.openapi.datamodels.low.context import Context
6
- from jentic.apitools.openapi.datamodels.low.fields import fixed_field
7
- from jentic.apitools.openapi.datamodels.low.model_builder import build_model
8
- from jentic.apitools.openapi.datamodels.low.sources import (
9
- FieldSource,
10
- KeySource,
11
- ValueSource,
12
- YAMLInvalidValue,
13
- YAMLValue,
14
- )
15
- from jentic.apitools.openapi.datamodels.low.v30.contact import Contact
16
- from jentic.apitools.openapi.datamodels.low.v30.contact import build as build_contact
17
- from jentic.apitools.openapi.datamodels.low.v30.license import License
18
- from jentic.apitools.openapi.datamodels.low.v30.license import build as build_license
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 .contact import Contact
10
+ from .contact import build as build_contact
11
+ from .license import License
12
+ from .license import build as build_license
19
13
 
20
14
 
21
15
  __all__ = ["Info", "build"]
@@ -85,18 +79,15 @@ def build(root: yaml.Node, context: Context | None = None) -> Info | ValueSource
85
79
  assert info.version.value == '1.0.1'
86
80
  assert info.contact.value.name.value == 'API Support'
87
81
  """
88
- # Initialize context once at the beginning
89
- if context is None:
90
- context = Context()
82
+ context = context or Context()
91
83
 
92
- if not isinstance(root, yaml.MappingNode):
93
- # Preserve invalid root data instead of returning None
94
- value = context.yaml_constructor.construct_object(root, deep=True)
95
- return ValueSource(value=value, value_node=root)
96
-
97
- # Use build_model to handle most fields
84
+ # Use build_model for initial construction
98
85
  info = build_model(root, Info, context=context)
99
86
 
87
+ # If build_model returned ValueSource (invalid node), return it immediately
88
+ if not isinstance(info, Info):
89
+ return info
90
+
100
91
  # Manually handle nested objects (contact, license)
101
92
  for key_node, value_node in root.value:
102
93
  key = context.yaml_constructor.construct_yaml_str(key_node)
@@ -2,16 +2,10 @@ from dataclasses import dataclass, field
2
2
 
3
3
  from ruamel import yaml
4
4
 
5
- from jentic.apitools.openapi.datamodels.low.context import Context
6
- from jentic.apitools.openapi.datamodels.low.fields import fixed_field
7
- from jentic.apitools.openapi.datamodels.low.model_builder import build_model
8
- from jentic.apitools.openapi.datamodels.low.sources import (
9
- FieldSource,
10
- KeySource,
11
- ValueSource,
12
- YAMLInvalidValue,
13
- YAMLValue,
14
- )
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
15
9
 
16
10
 
17
11
  __all__ = ["License", "build"]