jentic-openapi-datamodels 1.0.0a17__tar.gz → 1.0.0a19__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jentic_openapi_datamodels-1.0.0a19/PKG-INFO +372 -0
- jentic_openapi_datamodels-1.0.0a19/README.md +358 -0
- {jentic_openapi_datamodels-1.0.0a17 → jentic_openapi_datamodels-1.0.0a19}/pyproject.toml +1 -1
- {jentic_openapi_datamodels-1.0.0a17 → jentic_openapi_datamodels-1.0.0a19}/src/jentic/apitools/openapi/datamodels/low/extractors.py +3 -3
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v30/__init__.py +76 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v30/builders/__init__.py +312 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v30/callback.py +131 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v30/components.py +236 -0
- {jentic_openapi_datamodels-1.0.0a17 → jentic_openapi_datamodels-1.0.0a19}/src/jentic/apitools/openapi/datamodels/low/v30/contact.py +4 -10
- {jentic_openapi_datamodels-1.0.0a17 → jentic_openapi_datamodels-1.0.0a19}/src/jentic/apitools/openapi/datamodels/low/v30/discriminator.py +4 -9
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v30/encoding.py +81 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v30/example.py +91 -0
- {jentic_openapi_datamodels-1.0.0a17 → jentic_openapi_datamodels-1.0.0a19}/src/jentic/apitools/openapi/datamodels/low/v30/external_documentation.py +4 -10
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v30/header.py +120 -0
- {jentic_openapi_datamodels-1.0.0a17 → jentic_openapi_datamodels-1.0.0a19}/src/jentic/apitools/openapi/datamodels/low/v30/info.py +14 -23
- {jentic_openapi_datamodels-1.0.0a17 → jentic_openapi_datamodels-1.0.0a19}/src/jentic/apitools/openapi/datamodels/low/v30/license.py +4 -10
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v30/link.py +141 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v30/media_type.py +110 -0
- {jentic_openapi_datamodels-1.0.0a17 → jentic_openapi_datamodels-1.0.0a19}/src/jentic/apitools/openapi/datamodels/low/v30/oauth_flow.py +4 -10
- {jentic_openapi_datamodels-1.0.0a17 → jentic_openapi_datamodels-1.0.0a19}/src/jentic/apitools/openapi/datamodels/low/v30/oauth_flows.py +7 -15
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v30/openapi.py +149 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v30/operation.py +134 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v30/parameter.py +123 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v30/path_item.py +125 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v30/paths.py +108 -0
- {jentic_openapi_datamodels-1.0.0a17 → jentic_openapi_datamodels-1.0.0a19}/src/jentic/apitools/openapi/datamodels/low/v30/reference.py +5 -9
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v30/request_body.py +108 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v30/response.py +104 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v30/responses.py +109 -0
- {jentic_openapi_datamodels-1.0.0a17 → jentic_openapi_datamodels-1.0.0a19}/src/jentic/apitools/openapi/datamodels/low/v30/schema.py +81 -97
- {jentic_openapi_datamodels-1.0.0a17 → jentic_openapi_datamodels-1.0.0a19}/src/jentic/apitools/openapi/datamodels/low/v30/security_requirement.py +14 -9
- {jentic_openapi_datamodels-1.0.0a17 → jentic_openapi_datamodels-1.0.0a19}/src/jentic/apitools/openapi/datamodels/low/v30/security_scheme.py +42 -22
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v30/server.py +111 -0
- {jentic_openapi_datamodels-1.0.0a17 → jentic_openapi_datamodels-1.0.0a19}/src/jentic/apitools/openapi/datamodels/low/v30/server_variable.py +4 -10
- {jentic_openapi_datamodels-1.0.0a17 → jentic_openapi_datamodels-1.0.0a19}/src/jentic/apitools/openapi/datamodels/low/v30/tag.py +8 -46
- {jentic_openapi_datamodels-1.0.0a17 → jentic_openapi_datamodels-1.0.0a19}/src/jentic/apitools/openapi/datamodels/low/v30/xml.py +4 -10
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/__init__.py +77 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/builders/__init__.py +347 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/callback.py +131 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/components.py +240 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/contact.py +61 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/discriminator.py +62 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/encoding.py +81 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/example.py +91 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/external_documentation.py +59 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/header.py +120 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/info.py +116 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/license.py +61 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/link.py +141 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/media_type.py +110 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/oauth_flow.py +65 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/oauth_flows.py +108 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/openapi.py +168 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/operation.py +133 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/parameter.py +123 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/path_item.py +125 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/paths.py +108 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/reference.py +65 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/request_body.py +108 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/response.py +104 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/responses.py +109 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/schema.py +498 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/security_requirement.py +106 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/security_scheme.py +129 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/server.py +111 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/server_variable.py +70 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/tag.py +63 -0
- jentic_openapi_datamodels-1.0.0a19/src/jentic/apitools/openapi/datamodels/low/v31/xml.py +54 -0
- jentic_openapi_datamodels-1.0.0a17/PKG-INFO +0 -211
- jentic_openapi_datamodels-1.0.0a17/README.md +0 -197
- jentic_openapi_datamodels-1.0.0a17/src/jentic/apitools/openapi/datamodels/low/model_builder.py +0 -129
- jentic_openapi_datamodels-1.0.0a17/src/jentic/apitools/openapi/datamodels/low/v30/__init__.py +0 -0
- {jentic_openapi_datamodels-1.0.0a17 → jentic_openapi_datamodels-1.0.0a19}/LICENSE +0 -0
- {jentic_openapi_datamodels-1.0.0a17 → jentic_openapi_datamodels-1.0.0a19}/NOTICE +0 -0
- {jentic_openapi_datamodels-1.0.0a17 → jentic_openapi_datamodels-1.0.0a19}/src/jentic/apitools/openapi/datamodels/low/__init__.py +0 -0
- {jentic_openapi_datamodels-1.0.0a17 → jentic_openapi_datamodels-1.0.0a19}/src/jentic/apitools/openapi/datamodels/low/context.py +0 -0
- {jentic_openapi_datamodels-1.0.0a17 → jentic_openapi_datamodels-1.0.0a19}/src/jentic/apitools/openapi/datamodels/low/fields.py +0 -0
- {jentic_openapi_datamodels-1.0.0a17 → jentic_openapi_datamodels-1.0.0a19}/src/jentic/apitools/openapi/datamodels/low/py.typed +0 -0
- {jentic_openapi_datamodels-1.0.0a17 → jentic_openapi_datamodels-1.0.0a19}/src/jentic/apitools/openapi/datamodels/low/sources.py +0 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jentic-openapi-datamodels
|
|
3
|
+
Version: 1.0.0a19
|
|
4
|
+
Summary: Jentic OpenAPI Data Models
|
|
5
|
+
Author: Jentic
|
|
6
|
+
Author-email: Jentic <hello@jentic.com>
|
|
7
|
+
License-Expression: Apache-2.0
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
License-File: NOTICE
|
|
10
|
+
Requires-Dist: ruamel-yaml~=0.18.15
|
|
11
|
+
Requires-Python: >=3.11
|
|
12
|
+
Project-URL: Homepage, https://github.com/jentic/jentic-openapi-tools
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# jentic-openapi-datamodels
|
|
16
|
+
|
|
17
|
+
Low-level data models for OpenAPI specifications.
|
|
18
|
+
|
|
19
|
+
This package provides data model classes for representing OpenAPI specification objects in Python.
|
|
20
|
+
|
|
21
|
+
## Features
|
|
22
|
+
|
|
23
|
+
**Low-Level Architecture**
|
|
24
|
+
- **Preserve Everything**: All data from source documents preserved exactly as-is, including invalid values
|
|
25
|
+
- **Zero Validation**: No validation or coercion during parsing - deferred to higher layers
|
|
26
|
+
- **Separation of Concerns**: Low-level model focuses on faithful representation; validation belongs elsewhere
|
|
27
|
+
|
|
28
|
+
**Source Tracking**
|
|
29
|
+
- **Complete Source Fidelity**: Every field tracks its exact YAML node location
|
|
30
|
+
- **Precise Error Reporting**: Line and column numbers via `start_mark` and `end_mark`
|
|
31
|
+
- **Metadata Preservation**: Full position tracking for accurate diagnostics
|
|
32
|
+
|
|
33
|
+
**Python Integration**
|
|
34
|
+
- **Python-Idiomatic Naming**: snake_case field names (e.g., `bearer_format`, `property_name`)
|
|
35
|
+
- **Spec-Aligned Mapping**: Automatic YAML name mapping (e.g., `bearerFormat` ↔ `bearer_format`)
|
|
36
|
+
- **Type Safety**: Full type hints with Generic types (`FieldSource[T]`, `KeySource[T]`, `ValueSource[T]`)
|
|
37
|
+
|
|
38
|
+
**Extensibility**
|
|
39
|
+
- **Extension Support**: Automatic extraction of OpenAPI `x-*` specification extensions
|
|
40
|
+
- **Unknown Field Tracking**: Capture typos and invalid fields for validation tools
|
|
41
|
+
- **Generic Builder Pattern**: Core `build_model()` function with object-specific builders for complex cases
|
|
42
|
+
|
|
43
|
+
**Performance**
|
|
44
|
+
- **Memory Efficient**: Immutable frozen dataclasses with `__slots__` for optimal memory usage
|
|
45
|
+
- **Shared Context**: All instances share a single YAML constructor for efficiency
|
|
46
|
+
|
|
47
|
+
**Version Support**
|
|
48
|
+
- **OpenAPI 2.0**: Planned for future release
|
|
49
|
+
- **OpenAPI 3.0.x**: Fully implemented
|
|
50
|
+
- **OpenAPI 3.1.x**: Fully implemented with JSON Schema 2020-12 support
|
|
51
|
+
- **OpenAPI 3.2.x**: Planned for future release
|
|
52
|
+
|
|
53
|
+
## Installation
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install jentic-openapi-datamodels
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Prerequisites:**
|
|
60
|
+
- Python 3.11+
|
|
61
|
+
|
|
62
|
+
## Quick Start
|
|
63
|
+
|
|
64
|
+
### Parsing OpenAPI 3.0 Documents
|
|
65
|
+
|
|
66
|
+
The main use case is parsing complete OpenAPI Documents:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from ruamel.yaml import YAML
|
|
70
|
+
from jentic.apitools.openapi.datamodels.low.v30 import build
|
|
71
|
+
|
|
72
|
+
# Parse OpenAPI document
|
|
73
|
+
yaml = YAML()
|
|
74
|
+
root = yaml.compose("""
|
|
75
|
+
openapi: 3.0.4
|
|
76
|
+
info:
|
|
77
|
+
title: Pet Store API
|
|
78
|
+
version: 1.0.0
|
|
79
|
+
paths:
|
|
80
|
+
/pets:
|
|
81
|
+
get:
|
|
82
|
+
summary: List all pets
|
|
83
|
+
responses:
|
|
84
|
+
'200':
|
|
85
|
+
description: A list of pets
|
|
86
|
+
""")
|
|
87
|
+
|
|
88
|
+
# Build OpenAPI document model
|
|
89
|
+
openapi_doc = build(root)
|
|
90
|
+
|
|
91
|
+
# Access document fields via Python naming (snake_case)
|
|
92
|
+
print(openapi_doc.openapi.value) # "3.0.4"
|
|
93
|
+
print(openapi_doc.info.value.title.value) # "Pet Store API"
|
|
94
|
+
print(openapi_doc.info.value.version.value) # "1.0.0"
|
|
95
|
+
|
|
96
|
+
# Access nested fields with full type safety
|
|
97
|
+
for path_key, path_item in openapi_doc.paths.value.path_items.items():
|
|
98
|
+
print(f"Path: {path_key.value}") # "/pets"
|
|
99
|
+
if path_item.value.get:
|
|
100
|
+
operation = path_item.value.get.value
|
|
101
|
+
print(f" Summary: {operation.summary.value}") # "List all pets"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Parsing OpenAPI 3.1 Documents with JSON Schema 2020-12
|
|
105
|
+
|
|
106
|
+
OpenAPI 3.1 fully supports JSON Schema 2020-12, including advanced features like boolean schemas, conditional validation and vocabulary declarations:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from ruamel.yaml import YAML
|
|
110
|
+
from jentic.apitools.openapi.datamodels.low.v31 import build
|
|
111
|
+
|
|
112
|
+
# Parse OpenAPI 3.1 document with JSON Schema 2020-12 features
|
|
113
|
+
yaml = YAML()
|
|
114
|
+
root = yaml.compose("""
|
|
115
|
+
openapi: 3.1.2
|
|
116
|
+
info:
|
|
117
|
+
title: Pet Store API
|
|
118
|
+
version: 1.0.0
|
|
119
|
+
paths:
|
|
120
|
+
/pets:
|
|
121
|
+
get:
|
|
122
|
+
responses:
|
|
123
|
+
'200':
|
|
124
|
+
description: Pet list
|
|
125
|
+
content:
|
|
126
|
+
application/json:
|
|
127
|
+
schema:
|
|
128
|
+
type: array
|
|
129
|
+
prefixItems:
|
|
130
|
+
- type: string
|
|
131
|
+
- type: integer
|
|
132
|
+
items: false
|
|
133
|
+
contains:
|
|
134
|
+
type: object
|
|
135
|
+
required: [id]
|
|
136
|
+
""")
|
|
137
|
+
|
|
138
|
+
openapi_doc = build(root)
|
|
139
|
+
|
|
140
|
+
# Access JSON Schema 2020-12 features
|
|
141
|
+
schema = openapi_doc.paths.value.path_items["/pets"].value.get.value.responses.value["200"].value.content.value["application/json"].value.schema
|
|
142
|
+
print(schema.prefix_items.value[0].type.value) # "string"
|
|
143
|
+
print(schema.items.value) # False (boolean schema)
|
|
144
|
+
print(schema.contains.value.required.value[0].value) # "id"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Parsing Individual Spec Objects
|
|
148
|
+
|
|
149
|
+
You can also parse individual OpenAPI specification objects:
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from ruamel.yaml import YAML
|
|
153
|
+
from jentic.apitools.openapi.datamodels.low.v30.security_scheme import build as build_security_scheme
|
|
154
|
+
|
|
155
|
+
# Parse a Security Scheme object
|
|
156
|
+
yaml = YAML()
|
|
157
|
+
root = yaml.compose("""
|
|
158
|
+
type: http
|
|
159
|
+
scheme: bearer
|
|
160
|
+
bearerFormat: JWT
|
|
161
|
+
""")
|
|
162
|
+
|
|
163
|
+
security_scheme = build_security_scheme(root)
|
|
164
|
+
|
|
165
|
+
# Access via Python field names (snake_case)
|
|
166
|
+
print(security_scheme.bearer_format.value) # "JWT"
|
|
167
|
+
|
|
168
|
+
# Access source location information
|
|
169
|
+
print(security_scheme.bearer_format.key_node.value) # "bearerFormat"
|
|
170
|
+
print(security_scheme.bearer_format.key_node.start_mark.line) # Line number
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
You can also parse OpenAPI 3.1 Schema objects with JSON Schema 2020-12 features:
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
from ruamel.yaml import YAML
|
|
177
|
+
from jentic.apitools.openapi.datamodels.low.v31.schema import build as build_schema
|
|
178
|
+
|
|
179
|
+
# Parse a Schema object with JSON Schema 2020-12 features
|
|
180
|
+
yaml = YAML()
|
|
181
|
+
root = yaml.compose("""
|
|
182
|
+
type: object
|
|
183
|
+
properties:
|
|
184
|
+
id:
|
|
185
|
+
type: integer
|
|
186
|
+
tags:
|
|
187
|
+
type: array
|
|
188
|
+
prefixItems:
|
|
189
|
+
- type: string
|
|
190
|
+
- type: string
|
|
191
|
+
items: false
|
|
192
|
+
patternProperties:
|
|
193
|
+
"^x-":
|
|
194
|
+
type: string
|
|
195
|
+
unevaluatedProperties: false
|
|
196
|
+
if:
|
|
197
|
+
properties:
|
|
198
|
+
premium:
|
|
199
|
+
const: true
|
|
200
|
+
then:
|
|
201
|
+
required: [support_tier]
|
|
202
|
+
""")
|
|
203
|
+
|
|
204
|
+
schema = build_schema(root)
|
|
205
|
+
|
|
206
|
+
# Access JSON Schema 2020-12 fields via Python naming (snake_case)
|
|
207
|
+
print(schema.properties.value["id"].type.value) # "integer"
|
|
208
|
+
print(schema.pattern_properties.value["^x-"].type.value) # "string"
|
|
209
|
+
print(schema.unevaluated_properties.value) # False
|
|
210
|
+
print(schema.prefix_items.value[0].type.value) # "string"
|
|
211
|
+
|
|
212
|
+
# Access conditional schema fields
|
|
213
|
+
print(schema.if_.value.properties.value["premium"].const.value) # True
|
|
214
|
+
print(schema.then_.value.required.value[0].value) # "support_tier"
|
|
215
|
+
|
|
216
|
+
# Access source location information
|
|
217
|
+
print(schema.type.key_node.start_mark.line) # Line number for "type" key
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Field Name Mapping
|
|
221
|
+
|
|
222
|
+
YAML `camelCase` fields automatically map to Python `snake_case`:
|
|
223
|
+
- `bearerFormat` → `bearer_format`
|
|
224
|
+
- `authorizationUrl` → `authorization_url`
|
|
225
|
+
- `openIdConnectUrl` → `openid_connect_url`
|
|
226
|
+
|
|
227
|
+
Special cases for Python reserved keywords and `$` fields:
|
|
228
|
+
|
|
229
|
+
- `in` → `in_`
|
|
230
|
+
- `if` → `if_`
|
|
231
|
+
- `then` → `then_`
|
|
232
|
+
- `else` → `else_`
|
|
233
|
+
- `not` → `not_`
|
|
234
|
+
- `$ref` → `ref_`
|
|
235
|
+
- `$id` → `id_`
|
|
236
|
+
- `$schema` → `schema_`
|
|
237
|
+
|
|
238
|
+
### Source Tracking
|
|
239
|
+
|
|
240
|
+
The package provides three immutable wrapper types for preserving source information:
|
|
241
|
+
|
|
242
|
+
**FieldSource[T]** - For OpenAPI fields with key-value pairs
|
|
243
|
+
- Used for: Fixed fields (`name`, `bearer_format`) and patterned fields (status codes, path items, schema properties)
|
|
244
|
+
- Tracks: Both key and value nodes
|
|
245
|
+
- Example: `SecurityScheme.bearer_format` is `FieldSource[str]`, response status codes are `FieldSource[Response]`
|
|
246
|
+
|
|
247
|
+
**KeySource[T]** - For dictionary keys
|
|
248
|
+
- Used for: keys in OpenAPI fields, `x-*` extensions and mapping dictionaries
|
|
249
|
+
- Tracks: Only key node
|
|
250
|
+
- Example: Keys in `Discriminator.mapping` are `KeySource[str]`
|
|
251
|
+
|
|
252
|
+
**ValueSource[T]** - For dictionary values and array items
|
|
253
|
+
- Used for: values in OpenAPI fields, in `x-*` extensions, mapping dictionaries and array items
|
|
254
|
+
- Tracks: Only value node
|
|
255
|
+
- Example: Values in `Discriminator.mapping` are `ValueSource[str]`
|
|
256
|
+
|
|
257
|
+
```python
|
|
258
|
+
from ruamel.yaml import YAML
|
|
259
|
+
from jentic.apitools.openapi.datamodels.low.v30 import build
|
|
260
|
+
|
|
261
|
+
# FieldSource: Fixed specification fields in OpenAPI document
|
|
262
|
+
yaml = YAML()
|
|
263
|
+
root = yaml.compose("""
|
|
264
|
+
openapi: 3.0.4
|
|
265
|
+
info:
|
|
266
|
+
title: Pet Store API
|
|
267
|
+
version: 1.0.0
|
|
268
|
+
paths: {}
|
|
269
|
+
""")
|
|
270
|
+
openapi_doc = build(root)
|
|
271
|
+
|
|
272
|
+
field = openapi_doc.info.value.title # FieldSource[str]
|
|
273
|
+
print(field.value) # "Pet Store API" - The actual value
|
|
274
|
+
print(field.key_node) # YAML node for "title"
|
|
275
|
+
print(field.value_node) # YAML node for "Pet Store API"
|
|
276
|
+
|
|
277
|
+
# KeySource/ValueSource: Dictionary fields (extensions, mapping)
|
|
278
|
+
# Extensions in OpenAPI objects use KeySource/ValueSource
|
|
279
|
+
root = yaml.compose("""
|
|
280
|
+
openapi: 3.0.4
|
|
281
|
+
info:
|
|
282
|
+
title: API
|
|
283
|
+
version: 1.0.0
|
|
284
|
+
x-custom: value
|
|
285
|
+
x-another: data
|
|
286
|
+
paths: {}
|
|
287
|
+
""")
|
|
288
|
+
openapi_doc = build(root)
|
|
289
|
+
|
|
290
|
+
for key, value in openapi_doc.info.value.extensions.items():
|
|
291
|
+
print(key.value) # KeySource[str]: "x-custom" or "x-another"
|
|
292
|
+
print(key.key_node) # YAML node for the extension key
|
|
293
|
+
print(value.value) # ValueSource: "value" or "data"
|
|
294
|
+
print(value.value_node) # YAML node for the extension value
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Location Ranges
|
|
298
|
+
|
|
299
|
+
Access precise location ranges within the source document using start_mark and end_mark:
|
|
300
|
+
|
|
301
|
+
```python
|
|
302
|
+
from ruamel.yaml import YAML
|
|
303
|
+
from jentic.apitools.openapi.datamodels.low.v30 import build
|
|
304
|
+
|
|
305
|
+
yaml_content = """
|
|
306
|
+
openapi: 3.0.4
|
|
307
|
+
info:
|
|
308
|
+
title: Pet Store API
|
|
309
|
+
version: 1.0.0
|
|
310
|
+
description: A sample Pet Store API
|
|
311
|
+
paths: {}
|
|
312
|
+
"""
|
|
313
|
+
|
|
314
|
+
yaml = YAML()
|
|
315
|
+
root = yaml.compose(yaml_content)
|
|
316
|
+
openapi_doc = build(root)
|
|
317
|
+
|
|
318
|
+
# Access location information for any field
|
|
319
|
+
field = openapi_doc.info.value.title
|
|
320
|
+
|
|
321
|
+
# Key location (e.g., "title")
|
|
322
|
+
print(f"Key start: line {field.key_node.start_mark.line}, col {field.key_node.start_mark.column}")
|
|
323
|
+
print(f"Key end: line {field.key_node.end_mark.line}, col {field.key_node.end_mark.column}")
|
|
324
|
+
|
|
325
|
+
# Value location (e.g., "Pet Store API")
|
|
326
|
+
print(f"Value start: line {field.value_node.start_mark.line}, col {field.value_node.start_mark.column}")
|
|
327
|
+
print(f"Value end: line {field.value_node.end_mark.line}, col {field.value_node.end_mark.column}")
|
|
328
|
+
|
|
329
|
+
# Full field range (from key start to value end)
|
|
330
|
+
start = field.key_node.start_mark
|
|
331
|
+
end = field.value_node.end_mark
|
|
332
|
+
print(f"Field range: ({start.line}:{start.column}) to ({end.line}:{end.column})")
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Invalid Data Handling
|
|
336
|
+
|
|
337
|
+
Low-level models preserve invalid data without validation:
|
|
338
|
+
|
|
339
|
+
```python
|
|
340
|
+
from ruamel.yaml import YAML
|
|
341
|
+
from jentic.apitools.openapi.datamodels.low.v30 import build
|
|
342
|
+
|
|
343
|
+
yaml = YAML()
|
|
344
|
+
root = yaml.compose("""
|
|
345
|
+
openapi: 3.0.4
|
|
346
|
+
info:
|
|
347
|
+
title: 123 # Intentionally wrong type for demonstration (should be string)
|
|
348
|
+
version: 1.0.0
|
|
349
|
+
paths: {}
|
|
350
|
+
""")
|
|
351
|
+
|
|
352
|
+
openapi_doc = build(root)
|
|
353
|
+
print(openapi_doc.info.value.title.value) # 123 (preserved as-is)
|
|
354
|
+
print(type(openapi_doc.info.value.title.value)) # <class 'int'>
|
|
355
|
+
|
|
356
|
+
# Invalid data is preserved with full source tracking for validation tools
|
|
357
|
+
print(openapi_doc.info.value.title.value_node.start_mark.line) # Line number
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Error Reporting
|
|
361
|
+
|
|
362
|
+
This architecture—where the low-level model preserves data without validation and validation tools consume
|
|
363
|
+
that data—allows the low-level model to remain simple while enabling sophisticated validation tools to provide
|
|
364
|
+
user-friendly error messages with exact source locations.
|
|
365
|
+
|
|
366
|
+
## Testing
|
|
367
|
+
|
|
368
|
+
Run the test suite:
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
uv run --package jentic-openapi-datamodels pytest packages/jentic-openapi-datamodels -v
|
|
372
|
+
```
|