otterapi 0.0.5__py3-none-any.whl → 0.0.6__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.
- README.md +581 -8
- otterapi/__init__.py +73 -0
- otterapi/cli.py +327 -29
- otterapi/codegen/__init__.py +115 -0
- otterapi/codegen/ast_utils.py +134 -5
- otterapi/codegen/client.py +1271 -0
- otterapi/codegen/codegen.py +1736 -0
- otterapi/codegen/dataframes.py +392 -0
- otterapi/codegen/emitter.py +473 -0
- otterapi/codegen/endpoints.py +2597 -343
- otterapi/codegen/pagination.py +1026 -0
- otterapi/codegen/schema.py +593 -0
- otterapi/codegen/splitting.py +1397 -0
- otterapi/codegen/types.py +1345 -0
- otterapi/codegen/utils.py +180 -1
- otterapi/config.py +1017 -24
- otterapi/exceptions.py +231 -0
- otterapi/openapi/__init__.py +46 -0
- otterapi/openapi/v2/__init__.py +86 -0
- otterapi/openapi/v2/spec.json +1607 -0
- otterapi/openapi/v2/v2.py +1776 -0
- otterapi/openapi/v3/__init__.py +131 -0
- otterapi/openapi/v3/spec.json +1651 -0
- otterapi/openapi/v3/v3.py +1557 -0
- otterapi/openapi/v3_1/__init__.py +133 -0
- otterapi/openapi/v3_1/spec.json +1411 -0
- otterapi/openapi/v3_1/v3_1.py +798 -0
- otterapi/openapi/v3_2/__init__.py +133 -0
- otterapi/openapi/v3_2/spec.json +1666 -0
- otterapi/openapi/v3_2/v3_2.py +777 -0
- otterapi/tests/__init__.py +3 -0
- otterapi/tests/fixtures/__init__.py +455 -0
- otterapi/tests/test_ast_utils.py +680 -0
- otterapi/tests/test_codegen.py +610 -0
- otterapi/tests/test_dataframe.py +1038 -0
- otterapi/tests/test_exceptions.py +493 -0
- otterapi/tests/test_openapi_support.py +616 -0
- otterapi/tests/test_openapi_upgrade.py +215 -0
- otterapi/tests/test_pagination.py +1101 -0
- otterapi/tests/test_splitting_config.py +319 -0
- otterapi/tests/test_splitting_integration.py +427 -0
- otterapi/tests/test_splitting_resolver.py +512 -0
- otterapi/tests/test_splitting_tree.py +525 -0
- otterapi-0.0.6.dist-info/METADATA +627 -0
- otterapi-0.0.6.dist-info/RECORD +48 -0
- {otterapi-0.0.5.dist-info → otterapi-0.0.6.dist-info}/WHEEL +1 -1
- otterapi/codegen/generator.py +0 -358
- otterapi/codegen/openapi_processor.py +0 -27
- otterapi/codegen/type_generator.py +0 -559
- otterapi-0.0.5.dist-info/METADATA +0 -54
- otterapi-0.0.5.dist-info/RECORD +0 -16
- {otterapi-0.0.5.dist-info → otterapi-0.0.6.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,798 @@
|
|
|
1
|
+
"""OpenAPI 3.1 specification models.
|
|
2
|
+
|
|
3
|
+
This module provides Pydantic models for the OpenAPI 3.1 specification,
|
|
4
|
+
which is aligned with JSON Schema 2020-12.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import re
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from typing import TYPE_CHECKING, Annotated, Any, Union
|
|
12
|
+
|
|
13
|
+
from pydantic import (
|
|
14
|
+
AnyUrl,
|
|
15
|
+
BaseModel,
|
|
16
|
+
ConfigDict,
|
|
17
|
+
Field,
|
|
18
|
+
PositiveFloat,
|
|
19
|
+
RootModel,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from ..v3_2 import v3_2
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Reference(BaseModel):
|
|
27
|
+
"""Reference object for OpenAPI 3.1."""
|
|
28
|
+
|
|
29
|
+
ref: str = Field(..., alias='$ref')
|
|
30
|
+
summary: str | None = None
|
|
31
|
+
description: str | None = None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Contact(BaseModel):
|
|
35
|
+
"""Contact information for the API."""
|
|
36
|
+
|
|
37
|
+
model_config = ConfigDict(extra='forbid')
|
|
38
|
+
|
|
39
|
+
name: str | None = None
|
|
40
|
+
url: str | None = None
|
|
41
|
+
email: str | None = (
|
|
42
|
+
None # Using str instead of EmailStr to avoid optional dependency
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class License(BaseModel):
|
|
47
|
+
"""License information for the API."""
|
|
48
|
+
|
|
49
|
+
model_config = ConfigDict(extra='forbid')
|
|
50
|
+
|
|
51
|
+
name: str
|
|
52
|
+
identifier: str | None = None
|
|
53
|
+
url: str | None = None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class ServerVariable(BaseModel):
|
|
57
|
+
"""Server variable for URL templating."""
|
|
58
|
+
|
|
59
|
+
model_config = ConfigDict(extra='forbid')
|
|
60
|
+
|
|
61
|
+
enum: list[str] | None = None
|
|
62
|
+
default: str
|
|
63
|
+
description: str | None = None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class Type(Enum):
|
|
67
|
+
"""JSON Schema types."""
|
|
68
|
+
|
|
69
|
+
array = 'array'
|
|
70
|
+
boolean = 'boolean'
|
|
71
|
+
integer = 'integer'
|
|
72
|
+
number = 'number'
|
|
73
|
+
object = 'object'
|
|
74
|
+
string = 'string'
|
|
75
|
+
null = 'null'
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class Discriminator(BaseModel):
|
|
79
|
+
"""Discriminator for polymorphism."""
|
|
80
|
+
|
|
81
|
+
propertyName: str
|
|
82
|
+
mapping: dict[str, str] | None = None
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class XML(BaseModel):
|
|
86
|
+
"""XML representation metadata."""
|
|
87
|
+
|
|
88
|
+
model_config = ConfigDict(extra='forbid')
|
|
89
|
+
|
|
90
|
+
name: str | None = None
|
|
91
|
+
namespace: AnyUrl | None = None
|
|
92
|
+
prefix: str | None = None
|
|
93
|
+
attribute: bool | None = False
|
|
94
|
+
wrapped: bool | None = False
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class Example(BaseModel):
|
|
98
|
+
"""Example object."""
|
|
99
|
+
|
|
100
|
+
model_config = ConfigDict(extra='forbid')
|
|
101
|
+
|
|
102
|
+
summary: str | None = None
|
|
103
|
+
description: str | None = None
|
|
104
|
+
value: Any | None = None
|
|
105
|
+
externalValue: str | None = None
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class Style(Enum):
|
|
109
|
+
"""Parameter style for simple parameters."""
|
|
110
|
+
|
|
111
|
+
simple = 'simple'
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class SecurityRequirement(RootModel[dict[str, list[str]]]):
|
|
115
|
+
"""Security requirement object."""
|
|
116
|
+
|
|
117
|
+
root: dict[str, list[str]]
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class ExternalDocumentation(BaseModel):
|
|
121
|
+
"""External documentation reference."""
|
|
122
|
+
|
|
123
|
+
model_config = ConfigDict(extra='forbid')
|
|
124
|
+
|
|
125
|
+
description: str | None = None
|
|
126
|
+
url: str
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class ExampleXORExamples(RootModel[Any]):
|
|
130
|
+
"""Ensures example and examples are mutually exclusive."""
|
|
131
|
+
|
|
132
|
+
root: Any = Field(..., description='Example and examples are mutually exclusive')
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class SchemaXORContent1(BaseModel):
|
|
136
|
+
"""Helper for schema/content mutual exclusion."""
|
|
137
|
+
|
|
138
|
+
pass
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class SchemaXORContent(RootModel[Any | SchemaXORContent1]):
|
|
142
|
+
"""Ensures schema and content are mutually exclusive."""
|
|
143
|
+
|
|
144
|
+
root: Any | SchemaXORContent1 = Field(
|
|
145
|
+
...,
|
|
146
|
+
description='Schema and content are mutually exclusive, at least one is required',
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class In(Enum):
|
|
151
|
+
"""Path parameter location."""
|
|
152
|
+
|
|
153
|
+
path = 'path'
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class Style1(Enum):
|
|
157
|
+
"""Path parameter styles."""
|
|
158
|
+
|
|
159
|
+
matrix = 'matrix'
|
|
160
|
+
label = 'label'
|
|
161
|
+
simple = 'simple'
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class Required(Enum):
|
|
165
|
+
"""Required enum for path parameters."""
|
|
166
|
+
|
|
167
|
+
bool_True = True
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class PathParameter(BaseModel):
|
|
171
|
+
"""Path parameter definition."""
|
|
172
|
+
|
|
173
|
+
in_: In | None = Field(None, alias='in')
|
|
174
|
+
style: Style1 | None = 'simple'
|
|
175
|
+
required: Required
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class In1(Enum):
|
|
179
|
+
"""Query parameter location."""
|
|
180
|
+
|
|
181
|
+
query = 'query'
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class Style2(Enum):
|
|
185
|
+
"""Query parameter styles."""
|
|
186
|
+
|
|
187
|
+
form = 'form'
|
|
188
|
+
spaceDelimited = 'spaceDelimited'
|
|
189
|
+
pipeDelimited = 'pipeDelimited'
|
|
190
|
+
deepObject = 'deepObject'
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class QueryParameter(BaseModel):
|
|
194
|
+
"""Query parameter definition."""
|
|
195
|
+
|
|
196
|
+
in_: In1 | None = Field(None, alias='in')
|
|
197
|
+
style: Style2 | None = 'form'
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class In2(Enum):
|
|
201
|
+
"""Header parameter location."""
|
|
202
|
+
|
|
203
|
+
header = 'header'
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class Style3(Enum):
|
|
207
|
+
"""Header parameter style."""
|
|
208
|
+
|
|
209
|
+
simple = 'simple'
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class HeaderParameter(BaseModel):
|
|
213
|
+
"""Header parameter definition."""
|
|
214
|
+
|
|
215
|
+
in_: In2 | None = Field(None, alias='in')
|
|
216
|
+
style: Style3 | None = 'simple'
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class In3(Enum):
|
|
220
|
+
"""Cookie parameter location."""
|
|
221
|
+
|
|
222
|
+
cookie = 'cookie'
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
class Style4(Enum):
|
|
226
|
+
"""Cookie parameter style."""
|
|
227
|
+
|
|
228
|
+
form = 'form'
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class CookieParameter(BaseModel):
|
|
232
|
+
"""Cookie parameter definition."""
|
|
233
|
+
|
|
234
|
+
in_: In3 | None = Field(None, alias='in')
|
|
235
|
+
style: Style4 | None = 'form'
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class Type1(Enum):
|
|
239
|
+
"""API Key security type."""
|
|
240
|
+
|
|
241
|
+
apiKey = 'apiKey'
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
class In4(Enum):
|
|
245
|
+
"""API Key location."""
|
|
246
|
+
|
|
247
|
+
header = 'header'
|
|
248
|
+
query = 'query'
|
|
249
|
+
cookie = 'cookie'
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class APIKeySecurityScheme(BaseModel):
|
|
253
|
+
"""API Key security scheme."""
|
|
254
|
+
|
|
255
|
+
model_config = ConfigDict(extra='forbid')
|
|
256
|
+
|
|
257
|
+
type: Type1
|
|
258
|
+
name: str
|
|
259
|
+
in_: In4 = Field(..., alias='in')
|
|
260
|
+
description: str | None = None
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
class Type2(Enum):
|
|
264
|
+
"""HTTP security type."""
|
|
265
|
+
|
|
266
|
+
http = 'http'
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class HTTPSecurityScheme1(BaseModel):
|
|
270
|
+
"""HTTP Bearer security scheme."""
|
|
271
|
+
|
|
272
|
+
model_config = ConfigDict(extra='forbid')
|
|
273
|
+
|
|
274
|
+
scheme: Annotated[str, Field(pattern=r'^[Bb][Ee][Aa][Rr][Ee][Rr]$')]
|
|
275
|
+
bearerFormat: str | None = None
|
|
276
|
+
description: str | None = None
|
|
277
|
+
type: Type2
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
class HTTPSecurityScheme2(BaseModel):
|
|
281
|
+
"""HTTP non-Bearer security scheme."""
|
|
282
|
+
|
|
283
|
+
model_config = ConfigDict(extra='forbid')
|
|
284
|
+
|
|
285
|
+
scheme: str
|
|
286
|
+
bearerFormat: str | None = None
|
|
287
|
+
description: str | None = None
|
|
288
|
+
type: Type2
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
class HTTPSecurityScheme(RootModel[HTTPSecurityScheme1 | HTTPSecurityScheme2]):
|
|
292
|
+
"""HTTP security scheme union."""
|
|
293
|
+
|
|
294
|
+
root: HTTPSecurityScheme1 | HTTPSecurityScheme2
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
class Type4(Enum):
|
|
298
|
+
"""OAuth2 security type."""
|
|
299
|
+
|
|
300
|
+
oauth2 = 'oauth2'
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
class Type5(Enum):
|
|
304
|
+
"""OpenID Connect security type."""
|
|
305
|
+
|
|
306
|
+
openIdConnect = 'openIdConnect'
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
class OpenIdConnectSecurityScheme(BaseModel):
|
|
310
|
+
"""OpenID Connect security scheme."""
|
|
311
|
+
|
|
312
|
+
model_config = ConfigDict(extra='forbid')
|
|
313
|
+
|
|
314
|
+
type: Type5
|
|
315
|
+
openIdConnectUrl: str
|
|
316
|
+
description: str | None = None
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
class ImplicitOAuthFlow(BaseModel):
|
|
320
|
+
"""OAuth2 implicit flow."""
|
|
321
|
+
|
|
322
|
+
model_config = ConfigDict(extra='forbid')
|
|
323
|
+
|
|
324
|
+
authorizationUrl: str
|
|
325
|
+
refreshUrl: str | None = None
|
|
326
|
+
scopes: dict[str, str]
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
class PasswordOAuthFlow(BaseModel):
|
|
330
|
+
"""OAuth2 password flow."""
|
|
331
|
+
|
|
332
|
+
model_config = ConfigDict(extra='forbid')
|
|
333
|
+
|
|
334
|
+
tokenUrl: str
|
|
335
|
+
refreshUrl: str | None = None
|
|
336
|
+
scopes: dict[str, str]
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
class ClientCredentialsFlow(BaseModel):
|
|
340
|
+
"""OAuth2 client credentials flow."""
|
|
341
|
+
|
|
342
|
+
model_config = ConfigDict(extra='forbid')
|
|
343
|
+
|
|
344
|
+
tokenUrl: str
|
|
345
|
+
refreshUrl: str | None = None
|
|
346
|
+
scopes: dict[str, str]
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
class AuthorizationCodeOAuthFlow(BaseModel):
|
|
350
|
+
"""OAuth2 authorization code flow."""
|
|
351
|
+
|
|
352
|
+
model_config = ConfigDict(extra='forbid')
|
|
353
|
+
|
|
354
|
+
authorizationUrl: str
|
|
355
|
+
tokenUrl: str
|
|
356
|
+
refreshUrl: str | None = None
|
|
357
|
+
scopes: dict[str, str]
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
class Callback(RootModel[dict[Annotated[str, Field(pattern=r'^x-')], Any]]):
|
|
361
|
+
"""Callback object."""
|
|
362
|
+
|
|
363
|
+
root: dict[Annotated[str, Field(pattern=r'^x-')], Any]
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
class Style5(Enum):
|
|
367
|
+
"""Encoding styles."""
|
|
368
|
+
|
|
369
|
+
form = 'form'
|
|
370
|
+
spaceDelimited = 'spaceDelimited'
|
|
371
|
+
pipeDelimited = 'pipeDelimited'
|
|
372
|
+
deepObject = 'deepObject'
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
class Info(BaseModel):
|
|
376
|
+
"""API metadata."""
|
|
377
|
+
|
|
378
|
+
model_config = ConfigDict(extra='forbid')
|
|
379
|
+
|
|
380
|
+
title: str
|
|
381
|
+
summary: str | None = None
|
|
382
|
+
description: str | None = None
|
|
383
|
+
termsOfService: str | None = None
|
|
384
|
+
contact: Contact | None = None
|
|
385
|
+
license: License | None = None
|
|
386
|
+
version: str
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
class Server(BaseModel):
|
|
390
|
+
"""Server object."""
|
|
391
|
+
|
|
392
|
+
model_config = ConfigDict(extra='forbid')
|
|
393
|
+
|
|
394
|
+
url: str
|
|
395
|
+
description: str | None = None
|
|
396
|
+
variables: dict[str, ServerVariable] | None = None
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
class Schema(BaseModel):
|
|
400
|
+
"""JSON Schema object for OpenAPI 3.1.
|
|
401
|
+
|
|
402
|
+
OpenAPI 3.1 uses JSON Schema 2020-12 with some modifications.
|
|
403
|
+
"""
|
|
404
|
+
|
|
405
|
+
model_config = ConfigDict(extra='forbid')
|
|
406
|
+
|
|
407
|
+
# Core JSON Schema keywords
|
|
408
|
+
title: str | None = None
|
|
409
|
+
multipleOf: PositiveFloat | None = None
|
|
410
|
+
maximum: float | None = None
|
|
411
|
+
exclusiveMaximum: float | None = None # Changed from boolean in 3.0
|
|
412
|
+
minimum: float | None = None
|
|
413
|
+
exclusiveMinimum: float | None = None # Changed from boolean in 3.0
|
|
414
|
+
maxLength: Annotated[int, Field(ge=0)] | None = None
|
|
415
|
+
minLength: Annotated[int, Field(ge=0)] | None = 0
|
|
416
|
+
pattern: str | None = None
|
|
417
|
+
maxItems: Annotated[int, Field(ge=0)] | None = None
|
|
418
|
+
minItems: Annotated[int, Field(ge=0)] | None = 0
|
|
419
|
+
uniqueItems: bool | None = False
|
|
420
|
+
maxProperties: Annotated[int, Field(ge=0)] | None = None
|
|
421
|
+
minProperties: Annotated[int, Field(ge=0)] | None = 0
|
|
422
|
+
required: list[str] | None = None
|
|
423
|
+
enum: list[Any] | None = None
|
|
424
|
+
|
|
425
|
+
# Type can now be an array for nullable types
|
|
426
|
+
type: Type | list[Type] | None = None
|
|
427
|
+
|
|
428
|
+
# Composition keywords
|
|
429
|
+
not_: Schema | Reference | None = Field(None, alias='not')
|
|
430
|
+
allOf: list[Schema | Reference] | None = None
|
|
431
|
+
oneOf: list[Schema | Reference] | None = None
|
|
432
|
+
anyOf: list[Schema | Reference] | None = None
|
|
433
|
+
|
|
434
|
+
# Array keywords
|
|
435
|
+
items: Schema | Reference | None = None
|
|
436
|
+
prefixItems: list[Schema | Reference] | None = None # New in 3.1
|
|
437
|
+
|
|
438
|
+
# Object keywords
|
|
439
|
+
properties: dict[str, Schema | Reference] | None = None
|
|
440
|
+
additionalProperties: Schema | Reference | bool | None = True
|
|
441
|
+
patternProperties: dict[str, Schema | Reference] | None = None # New in 3.1
|
|
442
|
+
|
|
443
|
+
# String keywords
|
|
444
|
+
format: str | None = None
|
|
445
|
+
|
|
446
|
+
# Metadata
|
|
447
|
+
description: str | None = None
|
|
448
|
+
default: Any | None = None
|
|
449
|
+
|
|
450
|
+
# OpenAPI-specific keywords
|
|
451
|
+
discriminator: Discriminator | None = None
|
|
452
|
+
readOnly: bool | None = False
|
|
453
|
+
writeOnly: bool | None = False
|
|
454
|
+
example: Any | None = None
|
|
455
|
+
examples: list[Any] | None = None # New in 3.1
|
|
456
|
+
externalDocs: ExternalDocumentation | None = None
|
|
457
|
+
deprecated: bool | None = False
|
|
458
|
+
xml: XML | None = None
|
|
459
|
+
|
|
460
|
+
# Note: nullable is removed in 3.1, use type arrays instead
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
class Tag(BaseModel):
|
|
464
|
+
"""Tag for API operations."""
|
|
465
|
+
|
|
466
|
+
model_config = ConfigDict(extra='forbid')
|
|
467
|
+
|
|
468
|
+
name: str
|
|
469
|
+
description: str | None = None
|
|
470
|
+
externalDocs: ExternalDocumentation | None = None
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
class OAuthFlows(BaseModel):
|
|
474
|
+
"""OAuth2 flows configuration."""
|
|
475
|
+
|
|
476
|
+
model_config = ConfigDict(extra='forbid')
|
|
477
|
+
|
|
478
|
+
implicit: ImplicitOAuthFlow | None = None
|
|
479
|
+
password: PasswordOAuthFlow | None = None
|
|
480
|
+
clientCredentials: ClientCredentialsFlow | None = None
|
|
481
|
+
authorizationCode: AuthorizationCodeOAuthFlow | None = None
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
class Link(BaseModel):
|
|
485
|
+
"""Link object for response links."""
|
|
486
|
+
|
|
487
|
+
model_config = ConfigDict(extra='forbid')
|
|
488
|
+
|
|
489
|
+
operationId: str | None = None
|
|
490
|
+
operationRef: str | None = None
|
|
491
|
+
parameters: dict[str, Any] | None = None
|
|
492
|
+
requestBody: Any | None = None
|
|
493
|
+
description: str | None = None
|
|
494
|
+
server: Server | None = None
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
class OAuth2SecurityScheme(BaseModel):
|
|
498
|
+
"""OAuth2 security scheme."""
|
|
499
|
+
|
|
500
|
+
model_config = ConfigDict(extra='forbid')
|
|
501
|
+
|
|
502
|
+
type: Type4
|
|
503
|
+
flows: OAuthFlows
|
|
504
|
+
description: str | None = None
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
class SecurityScheme(
|
|
508
|
+
RootModel[
|
|
509
|
+
APIKeySecurityScheme
|
|
510
|
+
| HTTPSecurityScheme
|
|
511
|
+
| OAuth2SecurityScheme
|
|
512
|
+
| OpenIdConnectSecurityScheme
|
|
513
|
+
]
|
|
514
|
+
):
|
|
515
|
+
"""Security scheme union."""
|
|
516
|
+
|
|
517
|
+
root: (
|
|
518
|
+
APIKeySecurityScheme
|
|
519
|
+
| HTTPSecurityScheme
|
|
520
|
+
| OAuth2SecurityScheme
|
|
521
|
+
| OpenIdConnectSecurityScheme
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
class Components(BaseModel):
|
|
526
|
+
"""Components object for reusable definitions."""
|
|
527
|
+
|
|
528
|
+
model_config = ConfigDict(extra='forbid')
|
|
529
|
+
|
|
530
|
+
schemas: (
|
|
531
|
+
dict[Annotated[str, Field(pattern=r'^[a-zA-Z0-9\.\-_]+$')], Schema | Reference]
|
|
532
|
+
| None
|
|
533
|
+
) = None
|
|
534
|
+
responses: (
|
|
535
|
+
dict[
|
|
536
|
+
Annotated[str, Field(pattern=r'^[a-zA-Z0-9\.\-_]+$')], Reference | Response
|
|
537
|
+
]
|
|
538
|
+
| None
|
|
539
|
+
) = None
|
|
540
|
+
parameters: (
|
|
541
|
+
dict[
|
|
542
|
+
Annotated[str, Field(pattern=r'^[a-zA-Z0-9\.\-_]+$')], Reference | Parameter
|
|
543
|
+
]
|
|
544
|
+
| None
|
|
545
|
+
) = None
|
|
546
|
+
examples: (
|
|
547
|
+
dict[Annotated[str, Field(pattern=r'^[a-zA-Z0-9\.\-_]+$')], Reference | Example]
|
|
548
|
+
| None
|
|
549
|
+
) = None
|
|
550
|
+
requestBodies: (
|
|
551
|
+
dict[
|
|
552
|
+
Annotated[str, Field(pattern=r'^[a-zA-Z0-9\.\-_]+$')],
|
|
553
|
+
Reference | RequestBody,
|
|
554
|
+
]
|
|
555
|
+
| None
|
|
556
|
+
) = None
|
|
557
|
+
headers: (
|
|
558
|
+
dict[Annotated[str, Field(pattern=r'^[a-zA-Z0-9\.\-_]+$')], Reference | Header]
|
|
559
|
+
| None
|
|
560
|
+
) = None
|
|
561
|
+
securitySchemes: (
|
|
562
|
+
dict[
|
|
563
|
+
Annotated[str, Field(pattern=r'^[a-zA-Z0-9\.\-_]+$')],
|
|
564
|
+
Reference | SecurityScheme,
|
|
565
|
+
]
|
|
566
|
+
| None
|
|
567
|
+
) = None
|
|
568
|
+
links: (
|
|
569
|
+
dict[Annotated[str, Field(pattern=r'^[a-zA-Z0-9\.\-_]+$')], Reference | Link]
|
|
570
|
+
| None
|
|
571
|
+
) = None
|
|
572
|
+
callbacks: (
|
|
573
|
+
dict[
|
|
574
|
+
Annotated[str, Field(pattern=r'^[a-zA-Z0-9\.\-_]+$')], Reference | Callback
|
|
575
|
+
]
|
|
576
|
+
| None
|
|
577
|
+
) = None
|
|
578
|
+
pathItems: (
|
|
579
|
+
dict[
|
|
580
|
+
Annotated[str, Field(pattern=r'^[a-zA-Z0-9\.\-_]+$')], Reference | PathItem
|
|
581
|
+
]
|
|
582
|
+
| None
|
|
583
|
+
) = None # New in 3.1
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
class Response(BaseModel):
|
|
587
|
+
"""Response object."""
|
|
588
|
+
|
|
589
|
+
model_config = ConfigDict(extra='forbid')
|
|
590
|
+
|
|
591
|
+
description: str
|
|
592
|
+
headers: dict[str, Header | Reference] | None = None
|
|
593
|
+
content: dict[str, MediaType] | None = None
|
|
594
|
+
links: dict[str, Link | Reference] | None = None
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
class MediaType(BaseModel):
|
|
598
|
+
"""Media type object."""
|
|
599
|
+
|
|
600
|
+
model_config = ConfigDict(extra='forbid')
|
|
601
|
+
|
|
602
|
+
schema_: Schema | Reference | None = Field(None, alias='schema')
|
|
603
|
+
example: Any | None = None
|
|
604
|
+
examples: dict[str, Example | Reference] | None = None
|
|
605
|
+
encoding: dict[str, Encoding] | None = None
|
|
606
|
+
|
|
607
|
+
|
|
608
|
+
class Header(BaseModel):
|
|
609
|
+
"""Header object."""
|
|
610
|
+
|
|
611
|
+
model_config = ConfigDict(extra='forbid')
|
|
612
|
+
|
|
613
|
+
description: str | None = None
|
|
614
|
+
required: bool | None = False
|
|
615
|
+
deprecated: bool | None = False
|
|
616
|
+
allowEmptyValue: bool | None = False
|
|
617
|
+
style: Style | None = 'simple'
|
|
618
|
+
explode: bool | None = None
|
|
619
|
+
allowReserved: bool | None = False
|
|
620
|
+
schema_: Schema | Reference | None = Field(None, alias='schema')
|
|
621
|
+
content: dict[str, MediaType] | None = None
|
|
622
|
+
example: Any | None = None
|
|
623
|
+
examples: dict[str, Example | Reference] | None = None
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
class Paths(RootModel[dict[str, 'PathItem']]):
|
|
627
|
+
"""Paths object.
|
|
628
|
+
|
|
629
|
+
Keys should be path templates (starting with /) or extensions (starting with x-).
|
|
630
|
+
"""
|
|
631
|
+
|
|
632
|
+
root: dict[str, PathItem]
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
class PathItem(BaseModel):
|
|
636
|
+
"""Path item object."""
|
|
637
|
+
|
|
638
|
+
model_config = ConfigDict(extra='forbid')
|
|
639
|
+
|
|
640
|
+
field_ref: str | None = Field(None, alias='$ref')
|
|
641
|
+
summary: str | None = None
|
|
642
|
+
description: str | None = None
|
|
643
|
+
get: Operation | None = None
|
|
644
|
+
put: Operation | None = None
|
|
645
|
+
post: Operation | None = None
|
|
646
|
+
delete: Operation | None = None
|
|
647
|
+
options: Operation | None = None
|
|
648
|
+
head: Operation | None = None
|
|
649
|
+
patch: Operation | None = None
|
|
650
|
+
trace: Operation | None = None
|
|
651
|
+
servers: list[Server] | None = None
|
|
652
|
+
parameters: list[Parameter | Reference] | None = None
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
class Operation(BaseModel):
|
|
656
|
+
"""Operation object."""
|
|
657
|
+
|
|
658
|
+
model_config = ConfigDict(extra='forbid')
|
|
659
|
+
|
|
660
|
+
tags: list[str] | None = None
|
|
661
|
+
summary: str | None = None
|
|
662
|
+
description: str | None = None
|
|
663
|
+
externalDocs: ExternalDocumentation | None = None
|
|
664
|
+
operationId: str | None = None
|
|
665
|
+
parameters: list[Parameter | Reference] | None = None
|
|
666
|
+
requestBody: RequestBody | Reference | None = None
|
|
667
|
+
responses: Responses | None = None # Made optional in 3.1
|
|
668
|
+
callbacks: dict[str, Callback | Reference] | None = None
|
|
669
|
+
deprecated: bool | None = False
|
|
670
|
+
security: list[SecurityRequirement] | None = None
|
|
671
|
+
servers: list[Server] | None = None
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
class Responses(RootModel[dict[str, Response | Reference]]):
|
|
675
|
+
"""Responses object containing response definitions by HTTP status code.
|
|
676
|
+
|
|
677
|
+
Keys are HTTP status codes (200, 400, etc.) or 'default'.
|
|
678
|
+
"""
|
|
679
|
+
|
|
680
|
+
pass
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
class Parameter(BaseModel):
|
|
684
|
+
"""Parameter object."""
|
|
685
|
+
|
|
686
|
+
model_config = ConfigDict(extra='forbid')
|
|
687
|
+
|
|
688
|
+
name: str
|
|
689
|
+
in_: str = Field(..., alias='in')
|
|
690
|
+
description: str | None = None
|
|
691
|
+
required: bool | None = False
|
|
692
|
+
deprecated: bool | None = False
|
|
693
|
+
allowEmptyValue: bool | None = False
|
|
694
|
+
style: str | None = None
|
|
695
|
+
explode: bool | None = None
|
|
696
|
+
allowReserved: bool | None = False
|
|
697
|
+
schema_: Schema | Reference | None = Field(None, alias='schema')
|
|
698
|
+
content: dict[str, MediaType] | None = None
|
|
699
|
+
example: Any | None = None
|
|
700
|
+
examples: dict[str, Example | Reference] | None = None
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
class RequestBody(BaseModel):
|
|
704
|
+
"""Request body object."""
|
|
705
|
+
|
|
706
|
+
model_config = ConfigDict(extra='forbid')
|
|
707
|
+
|
|
708
|
+
description: str | None = None
|
|
709
|
+
content: dict[str, MediaType]
|
|
710
|
+
required: bool | None = False
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
class Encoding(BaseModel):
|
|
714
|
+
"""Encoding object."""
|
|
715
|
+
|
|
716
|
+
model_config = ConfigDict(extra='forbid')
|
|
717
|
+
|
|
718
|
+
contentType: str | None = None
|
|
719
|
+
headers: dict[str, Header | Reference] | None = None
|
|
720
|
+
style: Style5 | None = None
|
|
721
|
+
explode: bool | None = None
|
|
722
|
+
allowReserved: bool | None = False
|
|
723
|
+
|
|
724
|
+
|
|
725
|
+
class Webhook(RootModel[dict[str, Union['PathItem', Reference]]]):
|
|
726
|
+
"""Webhook object (new in OpenAPI 3.1)."""
|
|
727
|
+
|
|
728
|
+
root: dict[str, PathItem | Reference]
|
|
729
|
+
|
|
730
|
+
|
|
731
|
+
class OpenAPI(BaseModel):
|
|
732
|
+
"""OpenAPI 3.1 root document."""
|
|
733
|
+
|
|
734
|
+
model_config = ConfigDict(extra='forbid')
|
|
735
|
+
|
|
736
|
+
openapi: Annotated[str, Field(pattern=r'^3\.1\.\d+(-.+)?$')] # Updated for 3.1.x
|
|
737
|
+
info: Info
|
|
738
|
+
jsonSchemaDialect: str | None = (
|
|
739
|
+
'https://spec.openapis.org/oas/3.1/dialect/base' # New in 3.1
|
|
740
|
+
)
|
|
741
|
+
servers: list[Server] | None = None
|
|
742
|
+
paths: Paths | None = None # Made optional in 3.1
|
|
743
|
+
webhooks: dict[str, PathItem | Reference] | None = None # New in 3.1
|
|
744
|
+
components: Components | None = None
|
|
745
|
+
security: list[SecurityRequirement] | None = None
|
|
746
|
+
tags: list[Tag] | None = None
|
|
747
|
+
externalDocs: ExternalDocumentation | None = None
|
|
748
|
+
|
|
749
|
+
def upgrade(self) -> tuple[v3_2.OpenAPI, list[str]]:
|
|
750
|
+
"""Upgrade this OpenAPI 3.1 document to OpenAPI 3.2.
|
|
751
|
+
|
|
752
|
+
Converts the current OpenAPI 3.1 specification to OpenAPI 3.2 format.
|
|
753
|
+
The conversion includes:
|
|
754
|
+
- Updating the openapi version string from 3.1.x to 3.2.0
|
|
755
|
+
- Updating the jsonSchemaDialect to the 3.2 dialect
|
|
756
|
+
- All other fields are compatible and transferred as-is
|
|
757
|
+
|
|
758
|
+
Returns:
|
|
759
|
+
v3_2.OpenAPI: The upgraded OpenAPI 3.2 document
|
|
760
|
+
"""
|
|
761
|
+
from ..v3_2 import v3_2
|
|
762
|
+
|
|
763
|
+
# Get the model as a dictionary with aliases (e.g., 'schema' instead of 'schema_')
|
|
764
|
+
data = self.model_dump(mode='json', by_alias=True, exclude_none=False)
|
|
765
|
+
|
|
766
|
+
# Update the openapi version from 3.1.x to 3.2.0
|
|
767
|
+
version_match = re.match(r'^3\.1\.(\d+)(-.+)?$', data['openapi'])
|
|
768
|
+
if version_match:
|
|
769
|
+
# Keep the patch version but change major.minor to 3.2
|
|
770
|
+
patch = version_match.group(1)
|
|
771
|
+
suffix = version_match.group(2) or ''
|
|
772
|
+
data['openapi'] = f'3.2.{patch}{suffix}'
|
|
773
|
+
else:
|
|
774
|
+
# Fallback to 3.2.0 if pattern doesn't match
|
|
775
|
+
data['openapi'] = '3.2.0'
|
|
776
|
+
|
|
777
|
+
# Update jsonSchemaDialect if it's the 3.1 default
|
|
778
|
+
if (
|
|
779
|
+
data.get('jsonSchemaDialect')
|
|
780
|
+
== 'https://spec.openapis.org/oas/3.1/dialect/base'
|
|
781
|
+
):
|
|
782
|
+
data['jsonSchemaDialect'] = 'https://spec.openapis.org/oas/3.2/dialect/base'
|
|
783
|
+
|
|
784
|
+
# Parse and validate with v3_2.OpenAPI model
|
|
785
|
+
return v3_2.OpenAPI.model_validate(data), []
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
# Rebuild models to resolve forward references
|
|
789
|
+
Schema.model_rebuild()
|
|
790
|
+
OpenAPI.model_rebuild()
|
|
791
|
+
Components.model_rebuild()
|
|
792
|
+
Response.model_rebuild()
|
|
793
|
+
MediaType.model_rebuild()
|
|
794
|
+
Paths.model_rebuild()
|
|
795
|
+
PathItem.model_rebuild()
|
|
796
|
+
Operation.model_rebuild()
|
|
797
|
+
Parameter.model_rebuild()
|
|
798
|
+
Header.model_rebuild()
|