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,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 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.0.
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 | Reference"] | 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.0.
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
@@ -0,0 +1,108 @@
1
+ from dataclasses import dataclass, field
2
+
3
+ from ruamel import yaml
4
+
5
+ from ..context import Context
6
+ from ..extractors import extract_extension_fields
7
+ from ..sources import KeySource, ValueSource, YAMLInvalidValue, YAMLValue
8
+ from .path_item import PathItem
9
+ from .path_item import build as build_path_item
10
+
11
+
12
+ __all__ = ["Paths", "build"]
13
+
14
+
15
+ @dataclass(frozen=True, slots=True)
16
+ class Paths:
17
+ """
18
+ Paths Object representation for OpenAPI 3.0.
19
+
20
+ Holds the relative paths to the individual endpoints and their operations.
21
+ The paths are appended to the server URL to construct the full URL.
22
+
23
+ Path field names MUST begin with a forward slash (/). Path templating is supported
24
+ (e.g., /users/{id}). When matching URLs, concrete (non-templated) paths are matched
25
+ before templated paths. Templated paths with the same hierarchy but different templated
26
+ names are not allowed.
27
+
28
+ Attributes:
29
+ root_node: The top-level node representing the entire Paths object in the original source file
30
+ paths: Map of path strings to Path Item Objects. Each key is a relative path that MUST begin
31
+ with a forward slash (/). Supports path templating with curly braces.
32
+ extensions: Specification extensions (x-* fields)
33
+ """
34
+
35
+ root_node: yaml.Node
36
+ paths: dict[KeySource[str], PathItem] = field(default_factory=dict)
37
+ extensions: dict[KeySource[str], ValueSource[YAMLValue]] = field(default_factory=dict)
38
+
39
+
40
+ def build(root: yaml.Node, context: Context | None = None) -> Paths | ValueSource[YAMLInvalidValue]:
41
+ """
42
+ Build a Paths object from a YAML node.
43
+
44
+ Preserves all source data as-is, regardless of type. This is a low-level/plumbing
45
+ model that provides complete source fidelity for inspection and validation.
46
+
47
+ Args:
48
+ root: The YAML node to parse (should be a MappingNode)
49
+ context: Optional parsing context. If None, a default context will be created.
50
+
51
+ Returns:
52
+ A Paths object if the node is valid, or a ValueSource containing
53
+ the invalid data if the root is not a MappingNode (preserving the invalid data
54
+ and its source location for validation).
55
+
56
+ Example:
57
+ from ruamel.yaml import YAML
58
+ yaml = YAML()
59
+ root = yaml.compose('''
60
+ /users:
61
+ get:
62
+ summary: Get all users
63
+ responses:
64
+ '200':
65
+ description: Success
66
+ /users/{id}:
67
+ get:
68
+ summary: Get user by ID
69
+ parameters:
70
+ - name: id
71
+ in: path
72
+ required: true
73
+ schema:
74
+ type: integer
75
+ responses:
76
+ '200':
77
+ description: Success
78
+ ''')
79
+ paths = build(root)
80
+ assert '/users' in {k.value for k in paths.paths.keys()}
81
+ """
82
+ context = context or Context()
83
+
84
+ # Check if root is a MappingNode, if not return ValueSource with invalid data
85
+ if not isinstance(root, yaml.MappingNode):
86
+ value = context.yaml_constructor.construct_object(root, deep=True)
87
+ return ValueSource(value=value, value_node=root)
88
+
89
+ # Extract extensions first
90
+ extensions = extract_extension_fields(root, context)
91
+ extension_properties = {k.value for k in extensions.keys()}
92
+
93
+ # Process each field to determine if it's a path (not an extension)
94
+ paths = {}
95
+
96
+ for key_node, value_node in root.value:
97
+ key = context.yaml_constructor.construct_yaml_str(key_node)
98
+
99
+ if key not in extension_properties and key.startswith("/"):
100
+ # Path field (starts with / and not an extension) - build as Path Item
101
+ paths[KeySource(value=key, key_node=key_node)] = build_path_item(value_node, context)
102
+
103
+ # Create and return the Paths object with collected data
104
+ return Paths(
105
+ root_node=root,
106
+ paths=paths,
107
+ extensions=extensions,
108
+ )
@@ -2,14 +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
- ValueSource,
11
- YAMLInvalidValue,
12
- )
5
+ from ..context import Context
6
+ from ..fields import fixed_field
7
+ from ..sources import FieldSource, ValueSource, YAMLInvalidValue
8
+ from .builders import build_model
13
9
 
14
10
 
15
11
  __all__ = ["Reference", "build"]
@@ -20,7 +16,7 @@ class Reference:
20
16
  """
21
17
  Reference Object representation for OpenAPI 3.0.
22
18
 
23
- A simple object to allow referencing other components in the OpenAPI document,
19
+ A simple object to allow referencing other components in the OpenAPI Description,
24
20
  internally and externally.
25
21
 
26
22
  Note: In OpenAPI 3.0, Reference Objects only have the $ref field.
@@ -0,0 +1,108 @@
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 .media_type import MediaType
10
+ from .reference import Reference
11
+ from .reference import build as build_reference
12
+
13
+
14
+ __all__ = ["RequestBody", "build", "build_request_body_or_reference"]
15
+
16
+
17
+ @dataclass(frozen=True, slots=True)
18
+ class RequestBody:
19
+ """
20
+ Request Body Object representation for OpenAPI 3.0.
21
+
22
+ Describes a single request body.
23
+
24
+ Attributes:
25
+ root_node: The top-level node representing the entire Request Body object in the original source file
26
+ description: A brief description of the request body. CommonMark syntax MAY be used for rich text representation.
27
+ content: The content of the request body. The key is a media type or media type range and the value describes it.
28
+ For requests that match multiple keys, only the most specific key is applicable.
29
+ required: Determines if the request body is required in the request. Defaults to false.
30
+ extensions: Specification extensions (x-* fields)
31
+ """
32
+
33
+ root_node: yaml.Node
34
+ description: FieldSource[str] | None = fixed_field()
35
+ content: FieldSource[dict[KeySource[str], "MediaType"]] | None = fixed_field()
36
+ required: FieldSource[bool] | 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
+ ) -> RequestBody | ValueSource[YAMLInvalidValue]:
43
+ """
44
+ Build a RequestBody 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
+ A RequestBody 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('''
62
+ description: user to add to the system
63
+ required: true
64
+ content:
65
+ application/json:
66
+ schema:
67
+ type: object
68
+ ''')
69
+ request_body = build(root)
70
+ assert request_body.description.value == 'user to add to the system'
71
+ """
72
+ context = context or Context()
73
+
74
+ # Use build_model for initial construction
75
+ request_body = build_model(root, RequestBody, context=context)
76
+
77
+ # If build_model returned ValueSource (invalid node), return it immediately
78
+ if not isinstance(request_body, RequestBody):
79
+ return request_body
80
+
81
+ return request_body
82
+
83
+
84
+ def build_request_body_or_reference(
85
+ node: yaml.Node, context: Context
86
+ ) -> RequestBody | Reference | ValueSource[YAMLInvalidValue]:
87
+ """
88
+ Build either a RequestBody or Reference from a YAML node.
89
+
90
+ This helper handles the polymorphic nature of OpenAPI where many fields
91
+ can contain either a RequestBody object or a Reference object ($ref).
92
+
93
+ Args:
94
+ node: The YAML node to parse
95
+ context: Parsing context
96
+
97
+ Returns:
98
+ A RequestBody, Reference, or ValueSource if the node is invalid
99
+ """
100
+ # Check if it's a reference (has $ref key)
101
+ if isinstance(node, yaml.MappingNode):
102
+ for key_node, _ in node.value:
103
+ key = context.yaml_constructor.construct_yaml_str(key_node)
104
+ if key == "$ref":
105
+ return build_reference(node, context)
106
+
107
+ # Otherwise, try to build as RequestBody
108
+ return build(node, context)
@@ -0,0 +1,104 @@
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 .link import Link
11
+ from .media_type import MediaType
12
+ from .reference import Reference
13
+ from .reference import build as build_reference
14
+
15
+
16
+ __all__ = ["Response", "build", "build_response_or_reference"]
17
+
18
+
19
+ @dataclass(frozen=True, slots=True)
20
+ class Response:
21
+ """
22
+ Response Object representation for OpenAPI 3.0.
23
+
24
+ Describes a single response from an API Operation, including design-time, static links
25
+ to operations based on the response.
26
+
27
+ Attributes:
28
+ root_node: The top-level node representing the entire Response object in the original source file
29
+ description: A description of the response. CommonMark syntax MAY be used for rich text representation.
30
+ headers: Maps a header name to its definition.
31
+ content: A map containing descriptions of potential response payloads. The key is a media type or media type range
32
+ and the value describes it. For responses that match multiple keys, only the most specific key is applicable.
33
+ links: A map of operations links that can be followed from the response. The key is a short name for the link,
34
+ following the naming constraints of the Components Object.
35
+ extensions: Specification extensions (x-* fields)
36
+ """
37
+
38
+ root_node: yaml.Node
39
+ description: FieldSource[str] | None = fixed_field()
40
+ headers: FieldSource[dict[KeySource[str], "Header | Reference"]] | None = fixed_field()
41
+ content: FieldSource[dict[KeySource[str], "MediaType"]] | None = fixed_field()
42
+ links: FieldSource[dict[KeySource[str], "Link | Reference"]] | None = fixed_field()
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
+ ) -> Response | ValueSource[YAMLInvalidValue]:
49
+ """
50
+ Build a Response 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
+ A Response 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('''
68
+ description: successful operation
69
+ content:
70
+ application/json:
71
+ schema:
72
+ type: object
73
+ ''')
74
+ response = build(root)
75
+ assert response.description.value == 'successful operation'
76
+ """
77
+ return build_model(root, Response, context=context)
78
+
79
+
80
+ def build_response_or_reference(
81
+ node: yaml.Node, context: Context
82
+ ) -> Response | Reference | ValueSource[YAMLInvalidValue]:
83
+ """
84
+ Build either a Response or Reference from a YAML node.
85
+
86
+ This helper handles the polymorphic nature of OpenAPI where many fields
87
+ can contain either a Response object or a Reference object ($ref).
88
+
89
+ Args:
90
+ node: The YAML node to parse
91
+ context: Parsing context
92
+
93
+ Returns:
94
+ A Response, Reference, or ValueSource if the node is invalid
95
+ """
96
+ # Check if it's a reference (has $ref key)
97
+ if isinstance(node, yaml.MappingNode):
98
+ for key_node, _ in node.value:
99
+ key = context.yaml_constructor.construct_yaml_str(key_node)
100
+ if key == "$ref":
101
+ return build_reference(node, context)
102
+
103
+ # Otherwise, try to build as Response
104
+ return build(node, context)