Coverage for src/configuration/openapi.py: 99%

352 statements  

« prev     ^ index     » next       coverage.py v7.6.10, created at 2025-03-25 22:39 -0400

1"""OpenAPI 3.1 Object for creating function calls and storing API info in postgres 

2Docs from https://swagger.io/specification""" 

3 

4# pylint: skip-file 

5import json 

6from collections import defaultdict 

7from contextvars import ContextVar 

8from typing import Any, Callable, List, Optional, cast 

9from uuid import NAMESPACE_URL, UUID, uuid4, uuid5 

10 

11from pydantic import ( # type: ignore 

12 AliasGenerator, 

13 BaseModel, 

14 ConfigDict, 

15 Field, 

16 field_validator, 

17 model_validator, 

18 validator, 

19) 

20from pydantic.alias_generators import to_camel, to_snake # type: ignore 

21from sqlalchemy import select 

22from sqlalchemy.orm import scoped_session 

23from sqlalchemy.orm.util import identity_key 

24from typing_extensions import Self 

25 

26from common.models import openapi_entity, openapi_server 

27 

28# context_session: ContextVar = ContextVar("session") 

29 

30# context_relationships: ContextVar = ContextVar("relationships") 

31 

32 

33# config_info: dict = defaultdict(lambda: defaultdict(dict)) 

34 

35 

36class ExampleObject(BaseModel): 

37 """An object grouping an internal or external example value with basic 

38 summary and description metadata. This object is typically used in fields 

39 named examples (plural), and is a referenceable alternative to older example 

40 (singular) fields that do not support referencing or metadata. 

41 

42 Examples allow demonstration of the usage of properties, parameters and 

43 objects within OpenAPI.""" 

44 

45 model_config = ConfigDict( 

46 alias_generator=AliasGenerator( 

47 validation_alias=to_snake, 

48 serialization_alias=to_camel, 

49 ), 

50 populate_by_name=True, 

51 extra="allow", 

52 ) 

53 """This object MAY be extended with Specification Extensions.""" 

54 

55 summary: str | None = None 

56 """Short description for the example.""" 

57 

58 description: str | None = None 

59 """Long description for the example.  

60 [CommonMark syntax](https://spec.commonmark.org/) 

61 MAY be used for rich text representation.""" 

62 

63 # TODO: parse as plaintext string 

64 value: object | None = None 

65 """Embedded literal example. The value field and externalValue field are  

66 mutually exclusive. To represent examples of media types that cannot  

67 naturally represented in JSON or YAML, use a string value to contain the  

68 example, escaping where necessary.""" 

69 

70 external_value: str | None = None 

71 """A URI that identifies the literal example.  

72 This provides the capability to reference examples that cannot easily be  

73 included in JSON or YAML documents. The value field and externalValue field  

74 are mutually exclusive. See the rules for resolving Relative References.""" 

75 

76 

77class ContactObject(BaseModel): 

78 """Contact information for the exposed API.""" 

79 

80 model_config = ConfigDict( 

81 alias_generator=AliasGenerator( 

82 validation_alias=to_snake, 

83 serialization_alias=to_camel, 

84 ), 

85 populate_by_name=True, 

86 extra="allow", 

87 ) 

88 """This object MAY be extended with Specification Extensions.""" 

89 

90 name: str | None = None 

91 """The identifying name of the contact person/organization.""" 

92 

93 url: str | None = None 

94 """The URI for the contact information. This MUST be in the form of a URI.""" 

95 

96 email: str | None = None 

97 """The email address of the contact person/organization.  

98 This MUST be in the form of an email address.""" 

99 

100 

101class LicenseObject(BaseModel): 

102 """The license information for the exposed API""" 

103 

104 model_config = ConfigDict( 

105 alias_generator=AliasGenerator( 

106 validation_alias=to_snake, 

107 serialization_alias=to_camel, 

108 ), 

109 populate_by_name=True, 

110 extra="allow", 

111 ) 

112 """This object MAY be extended with Specification Extensions.""" 

113 

114 name: str 

115 """REQUIRED. The license name used for the API.""" 

116 

117 identifier: str | None = None 

118 """An [SPDX](https://spdx.org/licenses/) license expression for the API.  

119 The identifier field is mutually exclusive of the url field.""" 

120 

121 url: str | None = None 

122 """A URI for the license used for the API.  

123 This MUST be in the form of a URI.  

124 The url field is mutually exclusive of the identifier field.""" 

125 

126 

127class InfoObject(BaseModel): 

128 """OpenAPI Info Object""" 

129 

130 model_config = ConfigDict( 

131 alias_generator=AliasGenerator( 

132 validation_alias=to_snake, 

133 serialization_alias=to_camel, 

134 ), 

135 populate_by_name=True, 

136 extra="allow", 

137 ) 

138 """This object MAY be extended with Specification Extensions.""" 

139 

140 title: str 

141 """REQUIRED. The title of the API.""" 

142 

143 version: str 

144 """REQUIRED. The version of the OpenAPI Document  

145 (which is distinct from the OpenAPI Specification version  

146 or the version of the API being described or the version  

147 of the OpenAPI Description).""" 

148 

149 summary: str | None = None 

150 """A short summary of the API.""" 

151 

152 description: str | None = None 

153 """A description of the API.  

154 [CommonMark syntax](https://spec.commonmark.org/)  

155 MAY be used for rich text representation.""" 

156 

157 terms_of_service: str | None = None 

158 """A URI for the Terms of Service for the API.  

159 This MUST be in the form of a URI.""" 

160 

161 contact: ContactObject | None = None 

162 """The contact information for the exposed API.""" 

163 

164 license_: LicenseObject | None = None 

165 """The license information for the exposed API""" 

166 

167 

168class ServerVariableObject(BaseModel): 

169 """An object representing a Server Variable 

170 for server URL template substitution.""" 

171 

172 model_config = ConfigDict( 

173 alias_generator=AliasGenerator( 

174 validation_alias=to_snake, 

175 serialization_alias=to_camel, 

176 ), 

177 populate_by_name=True, 

178 extra="allow", 

179 ) 

180 """This object MAY be extended with Specification Extensions.""" 

181 

182 default: str 

183 """REQUIRED. The default value to use for substitution,  

184 which SHALL be sent if an alternate value is not supplied.  

185 If the enum is defined, the value MUST exist in the enum's values.  

186 Note that this behavior is different from the Schema Object's default keyword,  

187 which documents the receiver's behavior  

188 rather than inserting the value into the data.""" 

189 

190 enum: List[str] | None = None 

191 """An enumeration of string values to be used  

192 if the substitution options are from a limited set.  

193 The array MUST NOT be empty.""" 

194 

195 description: str | None = None 

196 """An optional description for the server variable.  

197 [CommonMark syntax](https://spec.commonmark.org/)  

198 MAY be used for rich text representation.""" 

199 

200 

201class ServerObject(BaseModel): 

202 """An object representing a Server.""" 

203 

204 model_config = ConfigDict( 

205 alias_generator=AliasGenerator( 

206 validation_alias=to_snake, 

207 serialization_alias=to_camel, 

208 ), 

209 populate_by_name=True, 

210 extra="allow", 

211 ) 

212 """This object MAY be extended with Specification Extensions.""" 

213 

214 url: str 

215 """REQUIRED. A URL to the target host.  

216 This URL supports Server Variables and MAY be relative,  

217 to indicate that the host location is relative to the location where  

218 the document containing the Server Object is being served.  

219 Variable substitutions will be made when a variable is named in {braces}.""" 

220 

221 # oas_uuid: UUID 

222 # """The UUID of the containing OpenAPI spec""" 

223 

224 # uuid: UUID 

225 # """a unique UUID for storing in the database""" 

226 

227 # @validator("uuid", always=True) 

228 # @classmethod 

229 # def validate_uuid(cls, value, values): 

230 # return uuid5(namespace=cls.uuid.NAMESPACE_URL, name=values["url"]) 

231 

232 # @validator("oas_uuid", always=True) 

233 # @classmethod 

234 # def validate_oas_uuid(cls, value): 

235 # return openapi_spec_id.get() 

236 

237 description: str | None = None 

238 """An optional string describing the host designated by the URL.  

239 [CommonMark syntax](https://spec.commonmark.org/) 

240 MAY be used for rich text representation.""" 

241 

242 variables: dict[str, ServerVariableObject] | None = None 

243 """A map between a variable name and its value.  

244 The value is used for substitution in the server's URL template.""" 

245 

246 # @model_validator(mode="after") 

247 # def finish(self) -> Self: 

248 # session: scoped_session = config_info[openapi_spec_id.get()]["session"] 

249 

250 # db_object = openapi_server.OpenAPIServer( 

251 # openapi_server_id=self.uuid, spec_id=self.oas_uuid 

252 # ) 

253 # if session.query(db_object).scalar() is None: 

254 # session.add(db_object) 

255 # return self 

256 

257 

258class ExternalDocumentationObject(BaseModel): 

259 """Allows referencing an external resource for extended documentation""" 

260 

261 model_config = ConfigDict( 

262 alias_generator=AliasGenerator( 

263 validation_alias=to_snake, 

264 serialization_alias=to_camel, 

265 ), 

266 populate_by_name=True, 

267 extra="allow", 

268 ) 

269 """This object MAY be extended with Specification Extensions.""" 

270 

271 url: str 

272 """REQUIRED. The URI for the target documentation.  

273 This MUST be in the form of a URI.""" 

274 

275 description: str | None = None 

276 """A description of the target documentation. 

277 [CommonMark syntax](https://spec.commonmark.org/) 

278 MAY be used for rich text representation.""" 

279 

280 

281class ParameterObject(BaseModel): 

282 """Describes a single operation parameter. 

283 

284 A unique parameter is defined by a combination of a name and location. 

285 

286 See [Appendix E](https://swagger.io/specification/#appendix-e-percent-encoding-and-form-media-types) 

287 for a detailed examination of percent-encoding concerns, 

288 including interactions with the 

289 application/x-www-form-urlencoded query string format. 

290 

291 # Parameter Locations 

292 

293 There are four possible parameter locations specified by the in field: 

294 

295 path - Used together with Path Templating, where the parameter value is 

296 actually part of the operation's URL. 

297 This does not include the host or base path of the API. 

298 For example, in /items/{itemId}, the path parameter is itemId. 

299 query - Parameters that are appended to the URL. 

300 For example, in /items?id=###, the query parameter is id. 

301 header - Custom headers that are expected as part of the request. 

302 Note that RFC7230 states header names are case insensitive. 

303 cookie - Used to pass a specific cookie value to the API. 

304 

305 # Fixed Fields 

306 

307 The rules for serialization of the parameter are specified in one of two ways. 

308 Parameter Objects MUST include either a content field or a schema field, 

309 but not both. See Appendix B for a discussion of converting values of 

310 various types to string representations. 

311 

312 # Common Fixed Fields 

313 """ 

314 

315 model_config = ConfigDict( 

316 alias_generator=AliasGenerator( 

317 validation_alias=to_snake, 

318 serialization_alias=to_camel, 

319 ), 

320 populate_by_name=True, 

321 extra="allow", 

322 ) 

323 """These fields MAY be used with either content or schema.""" 

324 

325 name: str 

326 """REQUIRED. The name of the parameter. Parameter names are case sensitive. 

327 

328 - If `in` is "path", the name field MUST correspond to  

329 a template expression occurring within the path field in the Paths Object.  

330 See Path Templating for further information. 

331 - If `in` is "header" and the name field is "Accept", "Content-Type" or  

332 "Authorization", the parameter definition SHALL be ignored. 

333 - For all other cases, the name corresponds to  

334 the parameter name used by the `in` field.""" 

335 

336 in_: str = Field(alias="in") 

337 """REQUIRED. The location of the parameter.  

338 Possible values are "query", "header", "path" or "cookie".""" 

339 

340 description: str | None = None 

341 """A brief description of the parameter.  

342 This could contain examples of use.  

343 [CommonMark syntax](https://spec.commonmark.org/) 

344 MAY be used for rich text representation.""" 

345 

346 required: Optional[bool] = False 

347 """Determines whether this parameter is mandatory. If the parameter location is "path", this field is REQUIRED and its value MUST be True. Otherwise, the field MAY be included and its default value is false.""" 

348 

349 deprecated: Optional[bool] = False 

350 

351 allow_empty_value: Optional[bool] = False 

352 """If True, clients MAY pass a zero-length string value in place of  

353 parameters that would otherwise be omitted entirely,  

354 which the server SHOULD interpret as the parameter being unused.  

355 Default value is false.  

356 If style is used, and if behavior is n/a (cannot be serialized),  

357 the value of allowEmptyValue SHALL be ignored.  

358 Interactions between this field and the parameter's Schema Object are  

359 implementation-defined. This field is valid only for query parameters.  

360 Use of this field is NOT RECOMMENDED,  

361 and it is likely to be removed in a later revision.""" 

362 

363 x_cuecode: str | None = Field(default=None, alias="x-cuecode") 

364 

365 # @model_validator(mode="after") 

366 # def finish(self) -> Self: 

367 # session: scoped_session = config_info[openapi_spec_id.get()]["session"] 

368 # noun_prompt = self.x_cuecode 

369 # if noun_prompt is None: 

370 # noun_prompt = self.description 

371 # if self.description is None: 

372 # noun_prompt = self.name 

373 # if ( 

374 # session.execute( 

375 # select(openapi_entity.OpenAPIEntity).where( 

376 # openapi_entity.OpenAPIEntity.noun_prompt == noun_prompt 

377 # ) 

378 # ).scalar() 

379 # is None 

380 # ): 

381 # session.add( 

382 # openapi_entity.OpenAPIEntity( 

383 # openapi_entity_id=uuid4(), 

384 # contained_in_oa_spec_id=openapi_spec_id.get(), 

385 # noun_prompt=noun_prompt, 

386 # ) 

387 # ) 

388 # return self 

389 

390 

391class ParameterObjectSchema(ParameterObject): 

392 model_config = ConfigDict( 

393 alias_generator=AliasGenerator( 

394 validation_alias=to_snake, 

395 serialization_alias=to_camel, 

396 ), 

397 populate_by_name=True, 

398 extra="allow", 

399 ) 

400 

401 style: str 

402 

403 # @model_validator(mode="before") 

404 # def validate_style(cls, values): 

405 # if not "style" in data: 

406 # if values["in"] == "query" or "cookie": 

407 # values["style"] = "form" 

408 # elif values["in"] == "path" or "header": 

409 # values["style"] = "simple" 

410 

411 explode: bool = False 

412 

413 allow_reserved: bool = False 

414 

415 schema_: dict[str, Any] | None = Field(default=None, alias="schema") 

416 """Schema for parameters""" 

417 

418 example: Any | None = None 

419 

420 examples: dict[str, ExampleObject] | None = None 

421 

422 

423class ParameterObjectContent(ParameterObject): 

424 model_config = ConfigDict( 

425 alias_generator=AliasGenerator( 

426 validation_alias=to_snake, 

427 serialization_alias=to_camel, 

428 ), 

429 populate_by_name=True, 

430 extra="allow", 

431 ) 

432 content: dict[str, "MediaTypeObject"] | None = None 

433 

434 

435class DiscriminatorObject(BaseModel): 

436 """When request bodies or response payloads may be one of a number of 

437 different schemas, a Discriminator Object gives a hint about the expected 

438 schema of the document. This hint can be used to aid in serialization, 

439 deserialization, and validation. The Discriminator Object does this by 

440 implicitly or explicitly associating the possible values of a named property 

441 with alternative schemas. 

442 

443 Note that discriminator MUST NOT change the validation outcome of the 

444 schema.""" 

445 

446 model_config = ConfigDict( 

447 alias_generator=AliasGenerator( 

448 validation_alias=to_snake, 

449 serialization_alias=to_camel, 

450 ), 

451 populate_by_name=True, 

452 extra="allow", 

453 ) 

454 """This object MAY be extended with Specification Extensions.""" 

455 

456 propety_name: str 

457 """REQUIRED. The name of the property in the payload that will hold the  

458 discriminating value. This property SHOULD be required in the payload  

459 schema, as the behavior when the property is absent is undefined.""" 

460 

461 mapping: dict[str, str] | None = None 

462 """An object to hold mappings between payload values and schema names or  

463 URI references.""" 

464 

465 

466class XMLObject(BaseModel): 

467 """A metadata object that allows for more fine-tuned XML model definitions. 

468 

469 When using arrays, XML element names are not inferred 

470 (for singular/plural forms) and the name field SHOULD be used to add that 

471 information.""" 

472 

473 model_config = ConfigDict( 

474 alias_generator=AliasGenerator( 

475 validation_alias=to_snake, 

476 serialization_alias=to_camel, 

477 ), 

478 populate_by_name=True, 

479 extra="allow", 

480 ) 

481 """This object MAY be extended with Specification Extensions.""" 

482 

483 name: str | None = None 

484 """Replaces the name of the element/attribute used for the described schema  

485 property. When defined within items, it will affect the name of the  

486 individual XML elements within the list. When defined alongside type being  

487 "array" (outside the items), it will affect the wrapping element if and only  

488 if wrapped is True. If wrapped is false, it will be ignored.""" 

489 

490 namespace: str | None = None 

491 """The URI of the namespace definition.  

492 Value MUST be in the form of a non-relative URI.""" 

493 

494 prefix: str | None = None 

495 """The prefix to be used for the name.""" 

496 

497 attribute: Optional[bool] = False 

498 """Declares whether the property definition translates to an attribute  

499 instead of an element. Default value is false.""" 

500 

501 wrapped: Optional[bool] = False 

502 """MAY be used only for an array definition.  

503 Signifies whether the array is wrapped  

504 (for example, <books><book/><book/></books>) or unwrapped (<book/><book/>).  

505 Default value is false.  

506 The definition takes effect only when defined alongside type being "array"  

507 (outside the items).""" 

508 

509 

510class SchemaObject(BaseModel): 

511 """https://swagger.io/specification/#schema-object""" 

512 

513 model_config = ConfigDict( 

514 alias_generator=AliasGenerator( 

515 validation_alias=to_snake, 

516 serialization_alias=to_camel, 

517 ), 

518 populate_by_name=True, 

519 extra="allow", 

520 ) 

521 

522 discriminator: DiscriminatorObject | None = None 

523 """Adds support for polymorphism.  

524 The discriminator is used to determine which of a set of schemas a payload  

525 is expected to satisfy.  

526 See [Composition and Inheritance](https://swagger.io/specification/#composition-and-inheritance-polymorphism)  

527 for more details.""" 

528 

529 xml: XMLObject | None = None 

530 """This MAY be used only on property schemas.  

531 It has no effect on root schemas.  

532 Adds additional metadata to describe the XML representation of this  

533 property.""" 

534 

535 external_docs: ExternalDocumentationObject | None = None 

536 """Additional external documentation for this schema.""" 

537 

538 example: object | None = None 

539 """A free-form field to include an example of an instance for this schema.  

540 To represent examples that cannot be naturally represented in JSON or YAML,  

541 a string value can be used to contain the example with escaping  

542 where necessary. 

543 

544 Deprecated: The example field has been deprecated in favor of the  

545 JSON Schema examples keyword. Use of example is discouraged,  

546 and later versions of this specification may remove it.""" 

547 

548 

549class HeaderObject(BaseModel): 

550 """Describes a single header for HTTP responses and for individual parts in 

551 multipart representations; see the relevant Response Object and 

552 Encoding Object documentation for restrictions on which headers can be 

553 described. 

554 

555 The Header Object follows the structure of the Parameter Object, 

556 including determining its serialization strategy based on whether schema or 

557 content is present, with the following changes: 

558 

559 1. `name` MUST NOT be specified, it is given in the corresponding headers 

560 map. 

561 2. `in` MUST NOT be specified, it is implicitly in header. 

562 3. All traits that are affected by the location MUST be applicable to a 

563 location of header (for example, style). This means that allowEmptyValue and 

564 allowReserved MUST NOT be used, and style, if used, MUST be limited to 

565 "simple". 

566 

567 # Fixed Fields 

568 ## Common Fixed Fields 

569 

570 These fields MAY be used with either content or schema.""" 

571 

572 model_config = ConfigDict( 

573 alias_generator=AliasGenerator( 

574 validation_alias=to_snake, 

575 serialization_alias=to_camel, 

576 ), 

577 populate_by_name=True, 

578 extra="allow", 

579 ) 

580 """This object MAY be extended with Specification Extensions.""" 

581 

582 description: str | None = None 

583 """A brief description of the header. This could contain examples of use.  

584 [CommonMark syntax](https://spec.commonmark.org/) 

585 MAY be used for rich text representation.""" 

586 

587 required: bool = False 

588 """Determines whether this header is mandatory.""" 

589 

590 deprecated: bool = False 

591 """Specifies that the header is deprecated and  

592 SHOULD be transitioned out of usage.""" 

593 

594 

595class HeaderObjectSchema(HeaderObject): 

596 style: str | None = "simple" 

597 """Describes how the header value will be serialized.  

598 The default (and only legal value for headers) is "simple".""" 

599 

600 explode: bool = False 

601 """When this is true, header values of type array or object generate a  

602 single header whose value is a comma-separated list of the array items or  

603 key-value pairs of the map, see Style Examples. For other data types this  

604 field has no effect. The default value is false.""" 

605 

606 schema_: SchemaObject | None = Field(alias="schema", default=None) 

607 

608 example: Any 

609 

610 examples: dict[str, ExampleObject] | None = None 

611 

612 

613class HeaderObjectContent(HeaderObject): 

614 content: dict[str, "MediaTypeObject"] | None = None 

615 """A map containing the representations for the header.  

616 The key is the media type and the value describes it.  

617 The map MUST only contain one entry.""" 

618 

619 

620class EncodingObject(BaseModel): 

621 """A single encoding definition applied to a single schema property. 

622 See Appendix B for a discussion of converting values of various types to 

623 string representations. 

624 

625 Properties are correlated with multipart parts using the name parameter of 

626 Content-Disposition: form-data, and with application/x-www-form-urlencoded 

627 using the query string parameter names. In both cases, their order is 

628 implementation-defined. 

629 

630 See Appendix E for a detailed examination of percent-encoding concerns for 

631 form media types. 

632 # Fixed Fields 

633 ## Common Fixed Fields 

634 

635 These fields MAY be used either with or without the RFC6570-style 

636 serialization fields defined in the next section below.""" 

637 

638 model_config = ConfigDict( 

639 alias_generator=AliasGenerator( 

640 validation_alias=to_snake, 

641 serialization_alias=to_camel, 

642 ), 

643 populate_by_name=True, 

644 extra="allow", 

645 ) 

646 """This object MAY be extended with Specification Extensions.""" 

647 

648 content_type: str | None = None 

649 """The Content-Type for encoding a specific property.  

650 The value is a comma-separated list, each element of which is either a  

651 specific media type (e.g. image/png) or a wildcard media type (e.g. image/*).  

652 Default value depends on the property type as shown in the table below.""" 

653 

654 headers: dict[str, HeaderObject] | None = None 

655 """A map allowing additional information to be provided as headers.  

656 Content-Type is described separately and SHALL be ignored in this section.  

657 This field SHALL be ignored if the request body media type is not a  

658 multipart.""" 

659 

660 

661class MediaTypeObject(BaseModel): 

662 """Each Media Type Object provides schema and examples for the media type 

663 identified by its key. 

664 

665 When example or examples are provided, the example SHOULD match the 

666 specified schema and be in the correct format as specified by the media 

667 type and its encoding. 

668 The example and examples fields are mutually exclusive, 

669 and if either is present it SHALL override any example in the schema. 

670 See [Working With Examples](https://swagger.io/specification/#working-with-examples) 

671 for further guidance regarding the different ways of specifying examples, 

672 including non-JSON/YAML values.""" 

673 

674 model_config = ConfigDict( 

675 alias_generator=AliasGenerator( 

676 validation_alias=to_snake, 

677 serialization_alias=to_camel, 

678 ), 

679 populate_by_name=True, 

680 extra="allow", 

681 ) 

682 """This object MAY be extended with Specification Extensions.""" 

683 

684 # TODO: make this always be parsed as a string 

685 example: object | None = None 

686 """Example of the media type""" 

687 

688 examples: dict[str, ExampleObject] | None = None 

689 """Examples of the media type""" 

690 

691 encoding: dict[str, EncodingObject] | None = None 

692 """A map between a property name and its encoding information.  

693 The key, being the property name, MUST exist in the schema as a property.  

694 The encoding field SHALL only apply to Request Body Objects,  

695 and only when the media type is multipart or  

696 application/x-www-form-urlencoded.  

697 If no Encoding Object is provided for a property,  

698 the behavior is determined by the default values documented for the  

699 Encoding Object.""" 

700 

701 schema_: dict[str, Any] | None = Field(alias="schema", default=None) 

702 """The schema defining the content of the request,  

703 response, parameter, or header.""" 

704 

705 

706class RequestBodyObject(BaseModel): 

707 """Describes a single request body.""" 

708 

709 model_config = ConfigDict( 

710 alias_generator=AliasGenerator( 

711 validation_alias=to_snake, 

712 serialization_alias=to_camel, 

713 ), 

714 populate_by_name=True, 

715 extra="allow", 

716 ) 

717 """This object MAY be extended with Specification Extensions.""" 

718 

719 content: dict[str, MediaTypeObject] 

720 """REQUIRED. The content of the request body.  

721 The key is a media type or media type range and the value describes it.  

722 For requests that match multiple keys,  

723 only the most specific key is applicable.  

724 e.g. \"text/plain\" overrides \"text/*\"""" 

725 

726 description: str | None = None 

727 """A brief description of the request body.  

728 This could contain examples of use.  

729 CommonMark syntax MAY be used for rich text representation.""" 

730 

731 required: Optional[bool] = False 

732 """Determines if the request body is required in the request.  

733 Defaults to false.""" 

734 

735 

736class LinkObject(BaseModel): 

737 """The Link Object represents a possible design-time link for a response. 

738 The presence of a link does not guarantee the caller's ability to 

739 successfully invoke it, rather it provides a known relationship and 

740 traversal mechanism between responses and other operations. 

741 

742 Unlike dynamic links (i.e. links provided in the response payload), 

743 the OAS linking mechanism does not require link information in the runtime 

744 response. 

745 

746 For computing links and providing instructions to execute them, 

747 a runtime expression is used for accessing values in an operation 

748 and using them as parameters while invoking the linked operation.""" 

749 

750 model_config = ConfigDict( 

751 alias_generator=AliasGenerator( 

752 validation_alias=to_snake, 

753 serialization_alias=to_camel, 

754 ), 

755 populate_by_name=True, 

756 extra="allow", 

757 ) 

758 """This object MAY be extended with Specification Extensions.""" 

759 

760 operation_ref: str | None = None 

761 

762 operation_id: str | None = None 

763 

764 # TODO: evaluate constants/expressions 

765 parameters: dict[str, object] | None = None 

766 """A map representing parameters to pass to an operation as specified with  

767 operationId or identified via operationRef. The key is the parameter name  

768 to be used (optionally qualified with the parameter location,  

769 e.g. path.id for an id parameter in the path),  

770 whereas the value can be a constant or an expression to be evaluated and  

771 passed to the linked operation.""" 

772 

773 # TODO: evaluate constants/expressions 

774 request_body: object | None = None 

775 """A literal value or {expression} to use as a request body when calling  

776 the target operation.""" 

777 

778 description: str | None = None 

779 """A description of the link. 

780 [CommonMark syntax](https://spec.commonmark.org/) 

781 MAY be used for rich text representation.""" 

782 

783 server: ServerObject | None = None 

784 

785 

786class ResponseObject(BaseModel): 

787 """Describes a single response from an API operation, 

788 including design-time, static links to operations based on the response.""" 

789 

790 model_config = ConfigDict( 

791 alias_generator=AliasGenerator( 

792 validation_alias=to_snake, 

793 serialization_alias=to_camel, 

794 ), 

795 populate_by_name=True, 

796 extra="allow", 

797 ) 

798 """This object MAY be extended with Specification Extensions.""" 

799 

800 description: str 

801 """REQUIRED. A description of the response.  

802 [CommonMark syntax](https://spec.commonmark.org/) 

803 MAY be used for rich text representation.""" 

804 

805 headers: dict[str, HeaderObject] | None = None 

806 """Maps a header name to its definition.  

807 RFC7230 states header names are case insensitive.  

808 If a response header is defined with the name "Content-Type",  

809 it SHALL be ignored.""" 

810 

811 content: dict[str, MediaTypeObject] | None = None 

812 """A map containing descriptions of potential response payloads.  

813 The key is a media type or media type range and the value describes it.  

814 For responses that match multiple keys, only the most specific key is  

815 applicable. e.g. \"text/plain\" overrides \"text/*\"""" 

816 

817 links: dict[str, LinkObject] | None = None 

818 """A map of operations links that can be followed from the response.  

819 The key of the map is a short name for the link,  

820 following the naming constraints of the names for Component Objects.""" 

821 

822 

823SecurityRequirementObjects = List[dict[str, List[str]]] 

824 

825 

826class OperationObject(BaseModel): 

827 """Describes a single API operation on a path.""" 

828 

829 model_config = ConfigDict( 

830 alias_generator=AliasGenerator( 

831 validation_alias=to_snake, 

832 serialization_alias=to_camel, 

833 ), 

834 populate_by_name=True, 

835 extra="allow", 

836 ) 

837 """This object MAY be extended with Specification Extensions.""" 

838 

839 tags: List[str] | None = None 

840 """A list of tags for API documentation control.  

841 Tags can be used for logical grouping of operations  

842 by resources or any other qualifier.""" 

843 

844 summary: str | None = None 

845 """A short summary of what the operation does.""" 

846 

847 description: str | None = None 

848 """ 

849 A verbose explanation of the operation behavior.  

850 [CommonMark syntax](https://spec.commonmark.org/) 

851 MAY be used for rich text representation. 

852 """ 

853 

854 external_docs: ExternalDocumentationObject | None = None 

855 """Additional external documentation for this operation.""" 

856 

857 operation_id: str | None = None 

858 """Unique string used to identify the operation.  

859 The id MUST be unique among all operations described in the API.  

860 The operationId value is case-sensitive.  

861 Tools and libraries MAY use the operationId to uniquely identify an  

862 operation, therefore, it is RECOMMENDED to follow  

863 common programming naming conventions.""" 

864 

865 parameters: List[ParameterObject] | None = None 

866 """A list of parameters that are applicable for this operation.  

867 If a parameter is already defined at the Path Item, the new definition will  

868 override it but can never remove it.  

869 The list MUST NOT include duplicated parameters.  

870 A unique parameter is defined by a combination of a name and location.  

871 The list can use the Reference Object to link to parameters that are  

872 defined in the OpenAPI Object's components.parameters.""" 

873 

874 request_body: RequestBodyObject | None = None 

875 """The request body applicable for this operation.  

876 The requestBody is fully supported in HTTP methods where the HTTP 1.1  

877 specification RFC7231 has explicitly defined semantics for request bodies.  

878 In other cases where the HTTP spec is vague (such as GET, HEAD and DELETE),  

879 requestBody is permitted but does not have well-defined semantics and  

880 SHOULD be avoided if possible.""" 

881 

882 responses: dict[str, ResponseObject] | None = None 

883 """The list of possible responses as they are returned from executing this  

884 operation.""" 

885 

886 callbacks: dict[str, dict[object, "PathItemObject"]] | None = None 

887 """A map of possible out-of band callbacks related to the parent operation.  

888 The key is a unique identifier for the Callback Object.  

889 Each value in the map is a Callback Object that describes a request that  

890 may be initiated by the API provider and the expected responses.""" 

891 

892 security: SecurityRequirementObjects | None = None 

893 """Each name MUST correspond to a security scheme which is declared in the  

894 Security Schemes under the Components Object.  

895 If the security scheme is of type "oauth2" or "openIdConnect",  

896 then the value is a list of scope names required for the execution,  

897 and the list MAY be empty if authorization does not require a specified  

898 scope. For other security scheme types, the array MAY contain a list of  

899 role names which are required for the execution, but are not otherwise  

900 defined or exchanged in-band.""" 

901 

902 servers: List[ServerObject] | None = None 

903 """An alternative servers array to service this operation.  

904 If a servers array is specified at the Path Item Object or OpenAPI Object  

905 level, it will be overridden by this value.""" 

906 

907 deprecated: bool | None = None 

908 """Declares this operation to be deprecated. Consumers SHOULD refrain from  

909 usage of the declared operation.""" 

910 

911 x_cuecode_prompt: str | None = Field(alias="x-cuecode-prompt", default=None) 

912 x_cuecode_skip: bool | None = Field(alias="x-cuecode-skip", default=False) 

913 

914 

915class PathItemObject(BaseModel): 

916 """Describes the operations available on a single path. 

917 A Path Item MAY be empty, due to 

918 [ACL constraints](https://swagger.io/specification/#security-filtering). 

919 The path itself is still exposed to the documentation viewer 

920 but they will not know which operations and parameters are available.""" 

921 

922 model_config = ConfigDict( 

923 alias_generator=AliasGenerator( 

924 validation_alias=to_snake, serialization_alias=to_camel 

925 ), 

926 populate_by_name=True, 

927 extra="allow", 

928 ) 

929 """This object MAY be extended with Specification Extensions.""" 

930 

931 summary: str | None = None 

932 """An optional string summary,  

933 intended to apply to all operations in this path.""" 

934 

935 description: str | None = None 

936 """An optional string description,  

937 intended to apply to all operations in this path.  

938 [CommonMark syntax](https://spec.commonmark.org/) 

939 MAY be used for rich text representation.""" 

940 

941 get: OperationObject | None = None 

942 """A definition of a GET operation on this path.""" 

943 

944 put: OperationObject | None = None 

945 """A definition of a PUT operation on this path.""" 

946 

947 post: OperationObject | None = None 

948 """A definition of a POST operation on this path.""" 

949 

950 delete: OperationObject | None = None 

951 """A definition of a DELETE operation on this path.""" 

952 

953 options: OperationObject | None = None 

954 """A definition of a OPTIONS operation on this path.""" 

955 

956 head: OperationObject | None = None 

957 """A definition of a HEAD operation on this path.""" 

958 

959 patch: OperationObject | None = None 

960 """A definition of a PATCH operation on this path.""" 

961 

962 trace: OperationObject | None = None 

963 """A definition of a TRACE operation on this path.""" 

964 

965 servers: List[ServerObject] | None = None 

966 """An alternative servers array to service all operations in this path.  

967 If a servers array is specified at the OpenAPI Object level,  

968 it will be overridden by this value.""" 

969 

970 parameters: List[ParameterObject] | None = None 

971 """A list of parameters that are applicable for all the operations  

972 described under this path. These parameters can be overridden at the  

973 operation level, but cannot be removed there. The list MUST NOT include  

974 duplicated parameters. A unique parameter is defined by a combination of a  

975 name and location. The list can use the Reference Object to link to  

976 parameters that are defined in the OpenAPI Object's  

977 components.parameters.""" 

978 

979 ref_: str | None = Field(alias="$ref", default=None) 

980 """Allows for a referenced definition of this path item.  

981 The value MUST be in the form of a URI,  

982 and the referenced structure MUST be in the form of a Path Item Object.  

983 In case a Path Item Object field appears both in the defined object  

984 and the referenced object, the behavior is undefined.  

985 See the rules for resolving  

986 [Relative References](https://swagger.io/specification/#relative-references-in-api-description-uris).""" 

987 

988 x_cuecode_prompt: str | None = Field(alias="x-cuecode-prompt", default=None) 

989 x_cuecode_skip: bool | None = Field(alias="x-cuecode-skip", default=False) 

990 

991 @model_validator(mode="after") 

992 def finish(self) -> Self: 

993 # TODO: After validation for Path Object 

994 return self 

995 

996 

997context_tag_uuids: ContextVar[dict] = ContextVar("tag_uuids") 

998context_tags: ContextVar[dict] = ContextVar("tags") 

999 

1000 

1001class TagObject(BaseModel): 

1002 """Tag Object""" 

1003 

1004 model_config = ConfigDict( 

1005 alias_generator=AliasGenerator( 

1006 validation_alias=to_snake, serialization_alias=to_camel 

1007 ), 

1008 populate_by_name=True, 

1009 extra="allow", 

1010 ) 

1011 name: str 

1012 """REQUIRED. The name of the tag.""" 

1013 

1014 description: str | None = None 

1015 """A description for the tag.  

1016 CommonMark syntax MAY be used for rich text representation.""" 

1017 

1018 x_cuecode: str | None = Field(default=None, alias="x-cuecode") 

1019 

1020 externalDocs: ExternalDocumentationObject | None = None 

1021 

1022 openapi_entity_id: UUID = Field(default_factory=lambda: uuid4()) 

1023 

1024 @model_validator(mode="after") 

1025 def finish(self) -> Self: 

1026 

1027 # tag_noun_prompt=self.x_cuecode 

1028 # if tag_noun_prompt is None: 

1029 # tag_noun_prompt = self.description 

1030 # if tag_noun_prompt is None: 

1031 # tag_noun_prompt = self.name 

1032 

1033 # session: scoped_session = context_session.get() 

1034 # entity = openapi_entity.OpenAPIEntity( 

1035 # openapi_entity_id = self.openapi_entity_id, 

1036 # contained_in_oa_spec_id = openapi_spec_id.get(), 

1037 # noun_prompt=tag_noun_prompt 

1038 # ) 

1039 # if session.execute( 

1040 # select(openapi_entity.OpenAPIEntity) 

1041 # .where(openapi_entity.OpenAPIEntity.noun_prompt == tag_noun_prompt) 

1042 # ).scalar() is None: 

1043 # session.add(entity) 

1044 # tag_map: dict = context_tag_uuids.get() 

1045 # tag_map[self.name] = self.openapi_entity_id 

1046 # context_tag_uuids.set(tag_map) 

1047 

1048 return self 

1049 

1050 

1051class OAuthFlowObject(BaseModel): 

1052 """Individual OAuth Flow Object""" 

1053 

1054 model_config = ConfigDict( 

1055 alias_generator=AliasGenerator( 

1056 validation_alias=to_snake, 

1057 serialization_alias=to_camel, 

1058 ), 

1059 populate_by_name=True, 

1060 extra="allow", 

1061 ) 

1062 

1063 authorization_url: str | None 

1064 """only applies to oauth2 (\"implicit\", \"authorizationCode\")  

1065 REQUIRED. The authorization URL to be used for this flow.  

1066 This MUST be in the form of a URL.  

1067 The OAuth2 standard requires the use of TLS.""" 

1068 

1069 token_url: str | None = None 

1070 """only applies to oauth2(\"password\",  

1071 \"clientCredentials\", \"authorizationCode\"). REQUIRED. 

1072 The token URL to be used for this flow. This MUST be in the form of a URL.  

1073 The OAuth2 standard requires the use of TLS.""" 

1074 

1075 refresh_url: str | None = None 

1076 """The URL to be used for obtaining refresh tokens.  

1077 This MUST be in the form of a URL.  

1078 The OAuth2 standard requires the use of TLS.""" 

1079 

1080 scopes: dict[str, str] = {} 

1081 """REQUIRED. The available scopes for the OAuth2 security scheme.  

1082 A map between the scope name and a short description for it.  

1083 The map MAY be empty.""" 

1084 

1085 

1086class OAuthFlowsObject(BaseModel): 

1087 """OAsuth Flows object""" 

1088 

1089 model_config = ConfigDict( 

1090 alias_generator=AliasGenerator( 

1091 validation_alias=to_snake, 

1092 serialization_alias=to_camel, 

1093 ), 

1094 populate_by_name=True, 

1095 extra="allow", 

1096 ) 

1097 

1098 implicit: OAuthFlowObject | None = None 

1099 password: OAuthFlowObject | None = None 

1100 client_credentials: OAuthFlowObject | None = None 

1101 authorization_code: OAuthFlowObject | None = None 

1102 

1103 

1104class SecuritySchemeObject(BaseModel): 

1105 """Security Scheme Object""" 

1106 

1107 model_config = ConfigDict( 

1108 alias_generator=AliasGenerator( 

1109 validation_alias=to_snake, 

1110 serialization_alias=to_camel, 

1111 ), 

1112 populate_by_name=True, 

1113 extra="allow", 

1114 ) 

1115 

1116 type: str 

1117 """REQUIRED: type of security scheme; Valid values are 'apiKey', 'http',  

1118 'mutualTLS', 'oath2', 'openIdConnect'.""" 

1119 

1120 description: str | None = None 

1121 

1122 name: str | None = None 

1123 """only applies to apiKey. REQUIRED.  

1124 The name of the header, query or cookie parameter to be used.""" 

1125 

1126 in_: str | None = Field(alias="in", default=None) 

1127 """only applies to apiKey. REQUIRED. The location of the API key.  

1128 Valid values are \"query\", \"header\" or \"cookie\".""" 

1129 

1130 scheme: str | None = None 

1131 """only applies to http. REQUIRED.  

1132 The name of the HTTP Authorization scheme to be used in the  

1133 Authorization header as defined in [RFC7235] Section 5.1.  

1134 The values used SHOULD be registered in the IANA Authentication Scheme  

1135 registry.""" 

1136 

1137 bearer_format: str | None = None 

1138 """only applies to http ("bearer). A hint to the client to identify how the  

1139 bearer token is formatted. Bearer tokens are usually generated by an  

1140 authorization server, so this information is primarily for documentation  

1141 purposes.""" 

1142 

1143 flows: OAuthFlowsObject | None = None 

1144 """only applies to oauth2. REQUIRED. An object containing configuration 

1145 information for the flow types supported""" 

1146 

1147 open_id_connect_url: str | None = None 

1148 """only applies to openIdConnect. REQUIRED.  

1149 OpenId Connect URL to discover OAuth2 configuration values.  

1150 This MUST be in the form of a URL.  

1151 The OpenID Connect standard requires the use of TLS.""" 

1152 

1153 

1154class ComponentsObject(BaseModel): 

1155 """Components Object Removing this might be beneficial, 

1156 since references are already resolved. Testing needed.""" 

1157 

1158 model_config = ConfigDict( 

1159 alias_generator=AliasGenerator( 

1160 validation_alias=to_snake, 

1161 serialization_alias=to_camel, 

1162 ), 

1163 populate_by_name=True, 

1164 extra="allow", 

1165 ) 

1166 

1167 schemas: dict[str, SchemaObject] | None = None 

1168 responses: dict[str, ResponseObject] | None = None 

1169 parameters: dict[str, ParameterObject] | None = None 

1170 examples: dict[str, ExampleObject] | None = None 

1171 request_bodies: dict[str, RequestBodyObject] | None = None 

1172 headers: dict[str, HeaderObject] | None = None 

1173 security_schemes: dict[str, SecuritySchemeObject] | None = None 

1174 

1175 

1176openapi_spec_id: ContextVar = ContextVar("openapi_spec_id") 

1177"""generate a new OpenAPI Spec UUID for storing in the database""" 

1178 

1179 

1180class OpenAPIObject(BaseModel): 

1181 """Deserialized OpenAPI 3.1 Specification""" 

1182 

1183 openapi_spec_uuid: UUID 

1184 

1185 # db_session: scoped_session 

1186 

1187 base_url: str 

1188 

1189 session_errors_encountered: bool = False 

1190 

1191 model_config = ConfigDict( 

1192 alias_generator=AliasGenerator( 

1193 validation_alias=to_snake, serialization_alias=to_camel 

1194 ), 

1195 populate_by_name=True, 

1196 extra="allow", 

1197 ) 

1198 """This object MAY be extended with Specification Extensions.""" 

1199 

1200 openapi: str 

1201 """REQUIRED. This string MUST be the version number of the OpenAPI 

1202 Specification that the OpenAPI Document uses. 

1203 The openapi field SHOULD be used by tooling to interpret the 

1204 OpenAPI Document. This is not related to the API info.version string.""" 

1205 

1206 info: InfoObject 

1207 """REQUIRED. Provides metadata about the API. 

1208 The metadata MAY be used by tooling as required.""" 

1209 

1210 paths: dict[str, PathItemObject] 

1211 """The available paths and operations for the API.""" 

1212 

1213 json_schema_dialect: str | None = None 

1214 """The default value for the $schema keyword within Schema Objects  

1215 contained within this OAS document. This MUST be in the form of a URI.""" 

1216 

1217 webhooks: dict[str, PathItemObject] | None = None 

1218 

1219 security: SecurityRequirementObjects | None = None 

1220 

1221 servers: List[ServerObject] 

1222 """An array of Server Objects,  

1223 which provide connectivity information to a target server.  

1224 If the servers field is not provided, or is an empty array,  

1225 the default value would be a Server Object with a url value of `/`.""" 

1226 

1227 tags: List[TagObject] | None = None 

1228 

1229 components: ComponentsObject | None = None