jentic-openapi-datamodels 1.0.0a18__py3-none-any.whl → 1.0.0a19__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.0a19.dist-info/METADATA +372 -0
  67. jentic_openapi_datamodels-1.0.0a19.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.0a19.dist-info}/WHEEL +0 -0
  72. {jentic_openapi_datamodels-1.0.0a18.dist-info → jentic_openapi_datamodels-1.0.0a19.dist-info}/licenses/LICENSE +0 -0
  73. {jentic_openapi_datamodels-1.0.0a18.dist-info → jentic_openapi_datamodels-1.0.0a19.dist-info}/licenses/NOTICE +0 -0
@@ -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 BooleanJSONSchema, 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.1.
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 | BooleanJSONSchema"] | 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)
@@ -0,0 +1,116 @@
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 .contact import Contact
10
+ from .contact import build as build_contact
11
+ from .license import License
12
+ from .license import build as build_license
13
+
14
+
15
+ __all__ = ["Info", "build"]
16
+
17
+
18
+ @dataclass(frozen=True, slots=True)
19
+ class Info:
20
+ """
21
+ Info Object representation for OpenAPI 3.1.
22
+
23
+ Provides metadata about the API. The metadata MAY be used by the clients if needed,
24
+ and MAY be presented in editing or documentation generation tools for convenience.
25
+
26
+ Attributes:
27
+ root_node: The top-level node representing the entire Info object in the original source file
28
+ title: REQUIRED. The title of the API.
29
+ summary: A short summary of the API (new in OpenAPI 3.1).
30
+ description: A description of the API. CommonMark syntax MAY be used for rich text representation.
31
+ termsOfService: A URL to the Terms of Service for the API. This MUST be in the form of a URL.
32
+ contact: The contact information for the exposed API.
33
+ license: The license information for the exposed API.
34
+ version: REQUIRED. The version of the OpenAPI document (which is distinct from the OpenAPI Specification version or the API implementation version).
35
+ extensions: Specification extensions (x-* fields)
36
+ """
37
+
38
+ root_node: yaml.Node
39
+ title: FieldSource[str] | None = fixed_field()
40
+ summary: FieldSource[str] | None = fixed_field()
41
+ description: FieldSource[str] | None = fixed_field()
42
+ termsOfService: FieldSource[str] | None = fixed_field()
43
+ contact: FieldSource[Contact] | None = fixed_field()
44
+ license: FieldSource[License] | None = fixed_field()
45
+ version: FieldSource[str] | None = fixed_field()
46
+ extensions: dict[KeySource[str], ValueSource[YAMLValue]] = field(default_factory=dict)
47
+
48
+
49
+ def build(root: yaml.Node, context: Context | None = None) -> Info | ValueSource[YAMLInvalidValue]:
50
+ """
51
+ Build an Info object from a YAML node.
52
+
53
+ Preserves all source data as-is, regardless of type. This is a low-level/plumbing
54
+ model that provides complete source fidelity for inspection and validation.
55
+
56
+ Args:
57
+ root: The YAML node to parse (should be a MappingNode)
58
+ context: Optional parsing context. If None, a default context will be created.
59
+
60
+ Returns:
61
+ An Info object if the node is valid, or a ValueSource containing
62
+ the invalid data if the root is not a MappingNode (preserving the invalid data
63
+ and its source location for validation).
64
+
65
+ Example:
66
+ from ruamel.yaml import YAML
67
+ yaml = YAML()
68
+ root = yaml.compose('''
69
+ title: Sample Pet Store App
70
+ version: 1.0.1
71
+ description: This is a sample server for a pet store.
72
+ contact:
73
+ name: API Support
74
+ email: support@example.com
75
+ license:
76
+ name: Apache 2.0
77
+ url: https://www.apache.org/licenses/LICENSE-2.0.html
78
+ ''')
79
+ info = build(root)
80
+ assert info.title.value == 'Sample Pet Store App'
81
+ assert info.version.value == '1.0.1'
82
+ assert info.contact.value.name.value == 'API Support'
83
+ """
84
+ context = context or Context()
85
+
86
+ # Use build_model for initial construction
87
+ info = build_model(root, Info, context=context)
88
+
89
+ # If build_model returned ValueSource (invalid node), return it immediately
90
+ if not isinstance(info, Info):
91
+ return info
92
+
93
+ # Manually handle nested objects (contact, license)
94
+ for key_node, value_node in root.value:
95
+ key = context.yaml_constructor.construct_yaml_str(key_node)
96
+
97
+ if key == "contact":
98
+ # Handle nested Contact object - child builder handles invalid nodes
99
+ # FieldSource will auto-unwrap ValueSource if child returns it for invalid data
100
+ contact = FieldSource(
101
+ value=build_contact(value_node, context=context),
102
+ key_node=key_node,
103
+ value_node=value_node,
104
+ )
105
+ info = replace(info, contact=contact)
106
+ elif key == "license":
107
+ # Handle nested License object - child builder handles invalid nodes
108
+ # FieldSource will auto-unwrap ValueSource if child returns it for invalid data
109
+ license_obj = FieldSource(
110
+ value=build_license(value_node, context=context),
111
+ key_node=key_node,
112
+ value_node=value_node,
113
+ )
114
+ info = replace(info, license=license_obj)
115
+
116
+ return info
@@ -0,0 +1,61 @@
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
+
10
+
11
+ __all__ = ["License", "build"]
12
+
13
+
14
+ @dataclass(frozen=True, slots=True)
15
+ class License:
16
+ """
17
+ License Object representation for OpenAPI 3.1.
18
+
19
+ License information for the exposed API.
20
+
21
+ Attributes:
22
+ root_node: The top-level node representing the entire License object in the original source file
23
+ name: REQUIRED. The license name used for the API.
24
+ identifier: An SPDX license expression for the API. Mutually exclusive with the url field (new in OpenAPI 3.1).
25
+ url: A URL to the license used for the API. Value MUST be in the format of a URL. Mutually exclusive with the identifier field.
26
+ extensions: Specification extensions (x-* fields)
27
+ """
28
+
29
+ root_node: yaml.Node
30
+ name: FieldSource[str] | None = fixed_field()
31
+ identifier: FieldSource[str] | None = fixed_field()
32
+ url: FieldSource[str] | None = fixed_field()
33
+ extensions: dict[KeySource[str], ValueSource[YAMLValue]] = field(default_factory=dict)
34
+
35
+
36
+ def build(
37
+ root: yaml.Node, context: Context | None = None
38
+ ) -> License | ValueSource[YAMLInvalidValue]:
39
+ """
40
+ Build a License object from a YAML node.
41
+
42
+ Preserves all source data as-is, regardless of type. This is a low-level/plumbing
43
+ model that provides complete source fidelity for inspection and validation.
44
+
45
+ Args:
46
+ root: The YAML node to parse (should be a MappingNode)
47
+ context: Optional parsing context. If None, a default context will be created.
48
+
49
+ Returns:
50
+ A License object if the node is valid, or a ValueSource containing
51
+ the invalid data if the root is not a MappingNode (preserving the invalid data
52
+ and its source location for validation).
53
+
54
+ Example:
55
+ from ruamel.yaml import YAML
56
+ yaml = YAML()
57
+ root = yaml.compose("name: MIT\\nurl: https://opensource.org/licenses/MIT")
58
+ license = build(root)
59
+ assert license.name.value == 'MIT'
60
+ """
61
+ return build_model(root, License, context=context)
@@ -0,0 +1,141 @@
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 .reference import Reference
10
+ from .reference import build as build_reference
11
+ from .server import Server
12
+ from .server import build as build_server
13
+
14
+
15
+ __all__ = ["Link", "build", "build_link_or_reference"]
16
+
17
+
18
+ @dataclass(frozen=True, slots=True)
19
+ class Link:
20
+ """
21
+ Link Object representation for OpenAPI 3.1.
22
+
23
+ The Link Object represents a possible design-time link for a response.
24
+
25
+ Attributes:
26
+ root_node: The top-level node representing the entire Link object in the original source file
27
+ operation_ref: A relative or absolute URI reference to an OAS operation.
28
+ operation_id: The name of an existing, resolvable OAS operation.
29
+ parameters: A map representing parameters to pass to an operation as specified with operationId
30
+ or identified via operationRef.
31
+ request_body: A literal value or {expression} to use as a request body when calling the target operation.
32
+ description: A description of the link. CommonMark syntax MAY be used for rich text representation.
33
+ server: A server object to be used by the target operation.
34
+ extensions: Specification extensions (x-* fields)
35
+ """
36
+
37
+ root_node: yaml.Node
38
+ operation_ref: FieldSource[str] | None = fixed_field(metadata={"yaml_name": "operationRef"})
39
+ operation_id: FieldSource[str] | None = fixed_field(metadata={"yaml_name": "operationId"})
40
+ parameters: FieldSource[dict[KeySource[str], ValueSource[YAMLValue]]] | None = fixed_field()
41
+ request_body: FieldSource[YAMLValue] | None = fixed_field(metadata={"yaml_name": "requestBody"})
42
+ description: FieldSource[str] | None = fixed_field()
43
+ server: FieldSource[Server] | None = fixed_field()
44
+ extensions: dict[KeySource[str], ValueSource[YAMLValue]] = field(default_factory=dict)
45
+
46
+
47
+ def build(root: yaml.Node, context: Context | None = None) -> Link | ValueSource[YAMLInvalidValue]:
48
+ """
49
+ Build a Link object from a YAML node.
50
+
51
+ Preserves all source data as-is, regardless of type. This is a low-level/plumbing
52
+ model that provides complete source fidelity for inspection and validation.
53
+
54
+ Args:
55
+ root: The YAML node to parse (should be a MappingNode)
56
+ context: Optional parsing context. If None, a default context will be created.
57
+
58
+ Returns:
59
+ A Link object if the node is valid, or a ValueSource containing
60
+ the invalid data if the root is not a MappingNode (preserving the invalid data
61
+ and its source location for validation).
62
+
63
+ Example:
64
+ from ruamel.yaml import YAML
65
+ yaml = YAML()
66
+ root = yaml.compose("operationId: getUserById\\nparameters:\\n userId: $response.body#/id")
67
+ link = build(root)
68
+ assert link.operation_id.value == 'getUserById'
69
+ """
70
+ context = context or Context()
71
+
72
+ # Use build_model for initial construction
73
+ link = build_model(root, Link, context=context)
74
+
75
+ # If build_model returned ValueSource (invalid node), return it immediately
76
+ if not isinstance(link, Link):
77
+ return link
78
+
79
+ # Manually handle nested server object and parameters dict
80
+ replacements = {}
81
+ for key_node, value_node in root.value:
82
+ key = context.yaml_constructor.construct_yaml_str(key_node)
83
+
84
+ if key == "server":
85
+ # Handle nested Server object - child builder handles invalid nodes
86
+ replacements["server"] = FieldSource(
87
+ value=build_server(value_node, context=context),
88
+ key_node=key_node,
89
+ value_node=value_node,
90
+ )
91
+ elif key == "parameters":
92
+ # Handle parameters map with KeySource/ValueSource wrapping
93
+ if isinstance(value_node, yaml.MappingNode):
94
+ params_dict: dict[KeySource[str], ValueSource[YAMLValue]] = {}
95
+ for param_key_node, param_value_node in value_node.value:
96
+ param_key = context.yaml_constructor.construct_yaml_str(param_key_node)
97
+ param_value = context.yaml_constructor.construct_object(
98
+ param_value_node, deep=True
99
+ )
100
+ params_dict[KeySource(value=param_key, key_node=param_key_node)] = ValueSource(
101
+ value=param_value, value_node=param_value_node
102
+ )
103
+ replacements["parameters"] = FieldSource(
104
+ value=params_dict, key_node=key_node, value_node=value_node
105
+ )
106
+ else:
107
+ # Not a mapping - preserve as-is for validation
108
+ replacements["parameters"] = build_field_source(key_node, value_node, context)
109
+
110
+ # Apply all replacements at once
111
+ if replacements:
112
+ link = replace(link, **replacements)
113
+
114
+ return link
115
+
116
+
117
+ def build_link_or_reference(
118
+ node: yaml.Node, context: Context
119
+ ) -> Link | Reference | ValueSource[YAMLInvalidValue]:
120
+ """
121
+ Build either a Link or Reference from a YAML node.
122
+
123
+ This helper handles the polymorphic nature of OpenAPI where many fields
124
+ can contain either a Link object or a Reference object ($ref).
125
+
126
+ Args:
127
+ node: The YAML node to parse
128
+ context: Parsing context
129
+
130
+ Returns:
131
+ A Link, Reference, or ValueSource if the node is invalid
132
+ """
133
+ # Check if it's a reference (has $ref key)
134
+ if isinstance(node, yaml.MappingNode):
135
+ for key_node, _ in node.value:
136
+ key = context.yaml_constructor.construct_yaml_str(key_node)
137
+ if key == "$ref":
138
+ return build_reference(node, context)
139
+
140
+ # Otherwise, try to build as Link
141
+ return build(node, context)
@@ -0,0 +1,110 @@
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 .encoding import Encoding
10
+ from .encoding import build as build_encoding
11
+ from .example import Example
12
+ from .reference import Reference
13
+ from .schema import BooleanJSONSchema, Schema
14
+
15
+
16
+ __all__ = ["MediaType", "build"]
17
+
18
+
19
+ @dataclass(frozen=True, slots=True)
20
+ class MediaType:
21
+ """
22
+ Media Type Object representation for OpenAPI 3.1.
23
+
24
+ Each Media Type Object provides schema and examples for the media type identified by its key.
25
+
26
+ Attributes:
27
+ root_node: The top-level node representing the entire Media Type object in the original source file
28
+ schema: The schema defining the content of the request, response, or parameter.
29
+ example: Example of the media type. The example SHOULD match the specified schema and encoding properties if present.
30
+ examples: Examples of the media type. Each example SHOULD contain a value in the correct format as specified in the parameter encoding.
31
+ encoding: A map between a property name and its encoding information. The key, being the property name,
32
+ MUST exist in the schema as a property. The encoding object SHALL only apply to requestBody objects
33
+ when the media type is multipart or application/x-www-form-urlencoded.
34
+ extensions: Specification extensions (x-* fields)
35
+ """
36
+
37
+ root_node: yaml.Node
38
+ schema: FieldSource["Schema | BooleanJSONSchema"] | None = fixed_field()
39
+ example: FieldSource[YAMLValue] | None = fixed_field()
40
+ examples: FieldSource[dict[KeySource[str], "Example | Reference"]] | None = fixed_field()
41
+ encoding: FieldSource[dict[KeySource[str], Encoding]] | None = fixed_field()
42
+ extensions: dict[KeySource[str], ValueSource[YAMLValue]] = field(default_factory=dict)
43
+
44
+
45
+ def build(
46
+ root: yaml.Node, context: Context | None = None
47
+ ) -> MediaType | ValueSource[YAMLInvalidValue]:
48
+ """
49
+ Build a MediaType object from a YAML node.
50
+
51
+ Preserves all source data as-is, regardless of type. This is a low-level/plumbing
52
+ model that provides complete source fidelity for inspection and validation.
53
+
54
+ Args:
55
+ root: The YAML node to parse (should be a MappingNode)
56
+ context: Optional parsing context. If None, a default context will be created.
57
+
58
+ Returns:
59
+ A MediaType object if the node is valid, or a ValueSource containing
60
+ the invalid data if the root is not a MappingNode (preserving the invalid data
61
+ and its source location for validation).
62
+
63
+ Example:
64
+ from ruamel.yaml import YAML
65
+ yaml = YAML()
66
+ root = yaml.compose('''
67
+ schema:
68
+ type: string
69
+ examples:
70
+ user:
71
+ value: John Doe
72
+ ''')
73
+ media_type = build(root)
74
+ assert media_type.schema.value.type.value == 'string'
75
+ """
76
+ context = context or Context()
77
+
78
+ # Use build_model for initial construction
79
+ media_type = build_model(root, MediaType, context=context)
80
+
81
+ # If build_model returned ValueSource (invalid node), return it immediately
82
+ if not isinstance(media_type, MediaType):
83
+ return media_type
84
+
85
+ # Manually handle nested complex fields
86
+ for key_node, value_node in root.value:
87
+ key = context.yaml_constructor.construct_yaml_str(key_node)
88
+
89
+ if key == "encoding":
90
+ # Handle encoding field - map of Encoding objects
91
+ if isinstance(value_node, yaml.MappingNode):
92
+ encoding_dict: dict[KeySource[str], Encoding | ValueSource[YAMLInvalidValue]] = {}
93
+ for encoding_key_node, encoding_value_node in value_node.value:
94
+ encoding_key = context.yaml_constructor.construct_yaml_str(encoding_key_node)
95
+ # Build Encoding - child builder handles invalid nodes
96
+ encoding_obj = build_encoding(encoding_value_node, context)
97
+ encoding_dict[KeySource(value=encoding_key, key_node=encoding_key_node)] = (
98
+ encoding_obj
99
+ )
100
+ encoding = FieldSource(
101
+ value=encoding_dict, key_node=key_node, value_node=value_node
102
+ )
103
+ media_type = replace(media_type, encoding=encoding)
104
+ else:
105
+ # Not a mapping - preserve as-is for validation
106
+ encoding = build_field_source(key_node, value_node, context)
107
+ media_type = replace(media_type, encoding=encoding)
108
+ break
109
+
110
+ return media_type
@@ -0,0 +1,65 @@
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
+
10
+
11
+ __all__ = ["OAuthFlow", "build"]
12
+
13
+
14
+ @dataclass(frozen=True, slots=True)
15
+ class OAuthFlow:
16
+ """
17
+ OAuth Flow Object representation for OpenAPI 3.1.
18
+
19
+ Configuration details for a supported OAuth Flow.
20
+
21
+ Attributes:
22
+ root_node: The top-level node representing the entire OAuth Flow object in the original source file
23
+ authorization_url: The authorization URL to be used for this flow. REQUIRED for implicit and authorization_code flows.
24
+ token_url: The token URL to be used for this flow. REQUIRED for password, client_credentials, and authorization_code flows.
25
+ refresh_url: The URL to be used for obtaining refresh tokens. This MUST be in the form of a URL.
26
+ scopes: The available scopes for the OAuth2 security scheme. A map between the scope name and a short description for it.
27
+ extensions: Specification extensions (x-* fields)
28
+ """
29
+
30
+ root_node: yaml.Node
31
+ authorization_url: FieldSource[str] | None = fixed_field(
32
+ metadata={"yaml_name": "authorizationUrl"}
33
+ )
34
+ token_url: FieldSource[str] | None = fixed_field(metadata={"yaml_name": "tokenUrl"})
35
+ refresh_url: FieldSource[str] | None = fixed_field(metadata={"yaml_name": "refreshUrl"})
36
+ scopes: FieldSource[dict[KeySource[str], ValueSource[str]]] | None = fixed_field()
37
+ extensions: dict[KeySource[str], ValueSource[YAMLValue]] = field(default_factory=dict)
38
+
39
+
40
+ def build(
41
+ root: yaml.Node, context: Context | None = None
42
+ ) -> OAuthFlow | ValueSource[YAMLInvalidValue]:
43
+ """
44
+ Build an OAuthFlow object from a YAML node.
45
+
46
+ Preserves all source data as-is, regardless of type. This is a low-level/plumbing
47
+ model that provides complete source fidelity for inspection and validation.
48
+
49
+ Args:
50
+ root: The YAML node to parse (should be a MappingNode)
51
+ context: Optional parsing context. If None, a default context will be created.
52
+
53
+ Returns:
54
+ An OAuthFlow object if the node is valid, or a ValueSource containing
55
+ the invalid data if the root is not a MappingNode (preserving the invalid data
56
+ and its source location for validation).
57
+
58
+ Example:
59
+ from ruamel.yaml import YAML
60
+ yaml = YAML()
61
+ root = yaml.compose("tokenUrl: https://example.com/oauth/token\\nscopes:\\n read: Read access")
62
+ oauth_flow = build(root)
63
+ assert oauth_flow.tokenUrl.value == 'https://example.com/oauth/token'
64
+ """
65
+ return build_model(root, OAuthFlow, context=context)