models.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. from enum import Enum
  2. from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Type, Union
  3. from fastapi._compat import (
  4. PYDANTIC_V2,
  5. CoreSchema,
  6. GetJsonSchemaHandler,
  7. JsonSchemaValue,
  8. _model_rebuild,
  9. with_info_plain_validator_function,
  10. )
  11. from fastapi.logger import logger
  12. from pydantic import AnyUrl, BaseModel, Field
  13. from typing_extensions import Annotated, Literal, TypedDict
  14. from typing_extensions import deprecated as typing_deprecated
  15. try:
  16. import email_validator
  17. assert email_validator # make autoflake ignore the unused import
  18. from pydantic import EmailStr
  19. except ImportError: # pragma: no cover
  20. class EmailStr(str): # type: ignore
  21. @classmethod
  22. def __get_validators__(cls) -> Iterable[Callable[..., Any]]:
  23. yield cls.validate
  24. @classmethod
  25. def validate(cls, v: Any) -> str:
  26. logger.warning(
  27. "email-validator not installed, email fields will be treated as str.\n"
  28. "To install, run: pip install email-validator"
  29. )
  30. return str(v)
  31. @classmethod
  32. def _validate(cls, __input_value: Any, _: Any) -> str:
  33. logger.warning(
  34. "email-validator not installed, email fields will be treated as str.\n"
  35. "To install, run: pip install email-validator"
  36. )
  37. return str(__input_value)
  38. @classmethod
  39. def __get_pydantic_json_schema__(
  40. cls, core_schema: CoreSchema, handler: GetJsonSchemaHandler
  41. ) -> JsonSchemaValue:
  42. return {"type": "string", "format": "email"}
  43. @classmethod
  44. def __get_pydantic_core_schema__(
  45. cls, source: Type[Any], handler: Callable[[Any], CoreSchema]
  46. ) -> CoreSchema:
  47. return with_info_plain_validator_function(cls._validate)
  48. class BaseModelWithConfig(BaseModel):
  49. if PYDANTIC_V2:
  50. model_config = {"extra": "allow"}
  51. else:
  52. class Config:
  53. extra = "allow"
  54. class Contact(BaseModelWithConfig):
  55. name: Optional[str] = None
  56. url: Optional[AnyUrl] = None
  57. email: Optional[EmailStr] = None
  58. class License(BaseModelWithConfig):
  59. name: str
  60. identifier: Optional[str] = None
  61. url: Optional[AnyUrl] = None
  62. class Info(BaseModelWithConfig):
  63. title: str
  64. summary: Optional[str] = None
  65. description: Optional[str] = None
  66. termsOfService: Optional[str] = None
  67. contact: Optional[Contact] = None
  68. license: Optional[License] = None
  69. version: str
  70. class ServerVariable(BaseModelWithConfig):
  71. enum: Annotated[Optional[List[str]], Field(min_length=1)] = None
  72. default: str
  73. description: Optional[str] = None
  74. class Server(BaseModelWithConfig):
  75. url: Union[AnyUrl, str]
  76. description: Optional[str] = None
  77. variables: Optional[Dict[str, ServerVariable]] = None
  78. class Reference(BaseModel):
  79. ref: str = Field(alias="$ref")
  80. class Discriminator(BaseModel):
  81. propertyName: str
  82. mapping: Optional[Dict[str, str]] = None
  83. class XML(BaseModelWithConfig):
  84. name: Optional[str] = None
  85. namespace: Optional[str] = None
  86. prefix: Optional[str] = None
  87. attribute: Optional[bool] = None
  88. wrapped: Optional[bool] = None
  89. class ExternalDocumentation(BaseModelWithConfig):
  90. description: Optional[str] = None
  91. url: AnyUrl
  92. # Ref JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation#name-type
  93. SchemaType = Literal[
  94. "array", "boolean", "integer", "null", "number", "object", "string"
  95. ]
  96. class Schema(BaseModelWithConfig):
  97. # Ref: JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-core.html#name-the-json-schema-core-vocabu
  98. # Core Vocabulary
  99. schema_: Optional[str] = Field(default=None, alias="$schema")
  100. vocabulary: Optional[str] = Field(default=None, alias="$vocabulary")
  101. id: Optional[str] = Field(default=None, alias="$id")
  102. anchor: Optional[str] = Field(default=None, alias="$anchor")
  103. dynamicAnchor: Optional[str] = Field(default=None, alias="$dynamicAnchor")
  104. ref: Optional[str] = Field(default=None, alias="$ref")
  105. dynamicRef: Optional[str] = Field(default=None, alias="$dynamicRef")
  106. defs: Optional[Dict[str, "SchemaOrBool"]] = Field(default=None, alias="$defs")
  107. comment: Optional[str] = Field(default=None, alias="$comment")
  108. # Ref: JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-core.html#name-a-vocabulary-for-applying-s
  109. # A Vocabulary for Applying Subschemas
  110. allOf: Optional[List["SchemaOrBool"]] = None
  111. anyOf: Optional[List["SchemaOrBool"]] = None
  112. oneOf: Optional[List["SchemaOrBool"]] = None
  113. not_: Optional["SchemaOrBool"] = Field(default=None, alias="not")
  114. if_: Optional["SchemaOrBool"] = Field(default=None, alias="if")
  115. then: Optional["SchemaOrBool"] = None
  116. else_: Optional["SchemaOrBool"] = Field(default=None, alias="else")
  117. dependentSchemas: Optional[Dict[str, "SchemaOrBool"]] = None
  118. prefixItems: Optional[List["SchemaOrBool"]] = None
  119. # TODO: uncomment and remove below when deprecating Pydantic v1
  120. # It generates a list of schemas for tuples, before prefixItems was available
  121. # items: Optional["SchemaOrBool"] = None
  122. items: Optional[Union["SchemaOrBool", List["SchemaOrBool"]]] = None
  123. contains: Optional["SchemaOrBool"] = None
  124. properties: Optional[Dict[str, "SchemaOrBool"]] = None
  125. patternProperties: Optional[Dict[str, "SchemaOrBool"]] = None
  126. additionalProperties: Optional["SchemaOrBool"] = None
  127. propertyNames: Optional["SchemaOrBool"] = None
  128. unevaluatedItems: Optional["SchemaOrBool"] = None
  129. unevaluatedProperties: Optional["SchemaOrBool"] = None
  130. # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-structural
  131. # A Vocabulary for Structural Validation
  132. type: Optional[Union[SchemaType, List[SchemaType]]] = None
  133. enum: Optional[List[Any]] = None
  134. const: Optional[Any] = None
  135. multipleOf: Optional[float] = Field(default=None, gt=0)
  136. maximum: Optional[float] = None
  137. exclusiveMaximum: Optional[float] = None
  138. minimum: Optional[float] = None
  139. exclusiveMinimum: Optional[float] = None
  140. maxLength: Optional[int] = Field(default=None, ge=0)
  141. minLength: Optional[int] = Field(default=None, ge=0)
  142. pattern: Optional[str] = None
  143. maxItems: Optional[int] = Field(default=None, ge=0)
  144. minItems: Optional[int] = Field(default=None, ge=0)
  145. uniqueItems: Optional[bool] = None
  146. maxContains: Optional[int] = Field(default=None, ge=0)
  147. minContains: Optional[int] = Field(default=None, ge=0)
  148. maxProperties: Optional[int] = Field(default=None, ge=0)
  149. minProperties: Optional[int] = Field(default=None, ge=0)
  150. required: Optional[List[str]] = None
  151. dependentRequired: Optional[Dict[str, Set[str]]] = None
  152. # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-vocabularies-for-semantic-c
  153. # Vocabularies for Semantic Content With "format"
  154. format: Optional[str] = None
  155. # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-the-conten
  156. # A Vocabulary for the Contents of String-Encoded Data
  157. contentEncoding: Optional[str] = None
  158. contentMediaType: Optional[str] = None
  159. contentSchema: Optional["SchemaOrBool"] = None
  160. # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-basic-meta
  161. # A Vocabulary for Basic Meta-Data Annotations
  162. title: Optional[str] = None
  163. description: Optional[str] = None
  164. default: Optional[Any] = None
  165. deprecated: Optional[bool] = None
  166. readOnly: Optional[bool] = None
  167. writeOnly: Optional[bool] = None
  168. examples: Optional[List[Any]] = None
  169. # Ref: OpenAPI 3.1.0: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#schema-object
  170. # Schema Object
  171. discriminator: Optional[Discriminator] = None
  172. xml: Optional[XML] = None
  173. externalDocs: Optional[ExternalDocumentation] = None
  174. example: Annotated[
  175. Optional[Any],
  176. typing_deprecated(
  177. "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
  178. "although still supported. Use examples instead."
  179. ),
  180. ] = None
  181. # Ref: https://json-schema.org/draft/2020-12/json-schema-core.html#name-json-schema-documents
  182. # A JSON Schema MUST be an object or a boolean.
  183. SchemaOrBool = Union[Schema, bool]
  184. class Example(TypedDict, total=False):
  185. summary: Optional[str]
  186. description: Optional[str]
  187. value: Optional[Any]
  188. externalValue: Optional[AnyUrl]
  189. if PYDANTIC_V2: # type: ignore [misc]
  190. __pydantic_config__ = {"extra": "allow"}
  191. else:
  192. class Config:
  193. extra = "allow"
  194. class ParameterInType(Enum):
  195. query = "query"
  196. header = "header"
  197. path = "path"
  198. cookie = "cookie"
  199. class Encoding(BaseModelWithConfig):
  200. contentType: Optional[str] = None
  201. headers: Optional[Dict[str, Union["Header", Reference]]] = None
  202. style: Optional[str] = None
  203. explode: Optional[bool] = None
  204. allowReserved: Optional[bool] = None
  205. class MediaType(BaseModelWithConfig):
  206. schema_: Optional[Union[Schema, Reference]] = Field(default=None, alias="schema")
  207. example: Optional[Any] = None
  208. examples: Optional[Dict[str, Union[Example, Reference]]] = None
  209. encoding: Optional[Dict[str, Encoding]] = None
  210. class ParameterBase(BaseModelWithConfig):
  211. description: Optional[str] = None
  212. required: Optional[bool] = None
  213. deprecated: Optional[bool] = None
  214. # Serialization rules for simple scenarios
  215. style: Optional[str] = None
  216. explode: Optional[bool] = None
  217. allowReserved: Optional[bool] = None
  218. schema_: Optional[Union[Schema, Reference]] = Field(default=None, alias="schema")
  219. example: Optional[Any] = None
  220. examples: Optional[Dict[str, Union[Example, Reference]]] = None
  221. # Serialization rules for more complex scenarios
  222. content: Optional[Dict[str, MediaType]] = None
  223. class Parameter(ParameterBase):
  224. name: str
  225. in_: ParameterInType = Field(alias="in")
  226. class Header(ParameterBase):
  227. pass
  228. class RequestBody(BaseModelWithConfig):
  229. description: Optional[str] = None
  230. content: Dict[str, MediaType]
  231. required: Optional[bool] = None
  232. class Link(BaseModelWithConfig):
  233. operationRef: Optional[str] = None
  234. operationId: Optional[str] = None
  235. parameters: Optional[Dict[str, Union[Any, str]]] = None
  236. requestBody: Optional[Union[Any, str]] = None
  237. description: Optional[str] = None
  238. server: Optional[Server] = None
  239. class Response(BaseModelWithConfig):
  240. description: str
  241. headers: Optional[Dict[str, Union[Header, Reference]]] = None
  242. content: Optional[Dict[str, MediaType]] = None
  243. links: Optional[Dict[str, Union[Link, Reference]]] = None
  244. class Operation(BaseModelWithConfig):
  245. tags: Optional[List[str]] = None
  246. summary: Optional[str] = None
  247. description: Optional[str] = None
  248. externalDocs: Optional[ExternalDocumentation] = None
  249. operationId: Optional[str] = None
  250. parameters: Optional[List[Union[Parameter, Reference]]] = None
  251. requestBody: Optional[Union[RequestBody, Reference]] = None
  252. # Using Any for Specification Extensions
  253. responses: Optional[Dict[str, Union[Response, Any]]] = None
  254. callbacks: Optional[Dict[str, Union[Dict[str, "PathItem"], Reference]]] = None
  255. deprecated: Optional[bool] = None
  256. security: Optional[List[Dict[str, List[str]]]] = None
  257. servers: Optional[List[Server]] = None
  258. class PathItem(BaseModelWithConfig):
  259. ref: Optional[str] = Field(default=None, alias="$ref")
  260. summary: Optional[str] = None
  261. description: Optional[str] = None
  262. get: Optional[Operation] = None
  263. put: Optional[Operation] = None
  264. post: Optional[Operation] = None
  265. delete: Optional[Operation] = None
  266. options: Optional[Operation] = None
  267. head: Optional[Operation] = None
  268. patch: Optional[Operation] = None
  269. trace: Optional[Operation] = None
  270. servers: Optional[List[Server]] = None
  271. parameters: Optional[List[Union[Parameter, Reference]]] = None
  272. class SecuritySchemeType(Enum):
  273. apiKey = "apiKey"
  274. http = "http"
  275. oauth2 = "oauth2"
  276. openIdConnect = "openIdConnect"
  277. class SecurityBase(BaseModelWithConfig):
  278. type_: SecuritySchemeType = Field(alias="type")
  279. description: Optional[str] = None
  280. class APIKeyIn(Enum):
  281. query = "query"
  282. header = "header"
  283. cookie = "cookie"
  284. class APIKey(SecurityBase):
  285. type_: SecuritySchemeType = Field(default=SecuritySchemeType.apiKey, alias="type")
  286. in_: APIKeyIn = Field(alias="in")
  287. name: str
  288. class HTTPBase(SecurityBase):
  289. type_: SecuritySchemeType = Field(default=SecuritySchemeType.http, alias="type")
  290. scheme: str
  291. class HTTPBearer(HTTPBase):
  292. scheme: Literal["bearer"] = "bearer"
  293. bearerFormat: Optional[str] = None
  294. class OAuthFlow(BaseModelWithConfig):
  295. refreshUrl: Optional[str] = None
  296. scopes: Dict[str, str] = {}
  297. class OAuthFlowImplicit(OAuthFlow):
  298. authorizationUrl: str
  299. class OAuthFlowPassword(OAuthFlow):
  300. tokenUrl: str
  301. class OAuthFlowClientCredentials(OAuthFlow):
  302. tokenUrl: str
  303. class OAuthFlowAuthorizationCode(OAuthFlow):
  304. authorizationUrl: str
  305. tokenUrl: str
  306. class OAuthFlows(BaseModelWithConfig):
  307. implicit: Optional[OAuthFlowImplicit] = None
  308. password: Optional[OAuthFlowPassword] = None
  309. clientCredentials: Optional[OAuthFlowClientCredentials] = None
  310. authorizationCode: Optional[OAuthFlowAuthorizationCode] = None
  311. class OAuth2(SecurityBase):
  312. type_: SecuritySchemeType = Field(default=SecuritySchemeType.oauth2, alias="type")
  313. flows: OAuthFlows
  314. class OpenIdConnect(SecurityBase):
  315. type_: SecuritySchemeType = Field(
  316. default=SecuritySchemeType.openIdConnect, alias="type"
  317. )
  318. openIdConnectUrl: str
  319. SecurityScheme = Union[APIKey, HTTPBase, OAuth2, OpenIdConnect, HTTPBearer]
  320. class Components(BaseModelWithConfig):
  321. schemas: Optional[Dict[str, Union[Schema, Reference]]] = None
  322. responses: Optional[Dict[str, Union[Response, Reference]]] = None
  323. parameters: Optional[Dict[str, Union[Parameter, Reference]]] = None
  324. examples: Optional[Dict[str, Union[Example, Reference]]] = None
  325. requestBodies: Optional[Dict[str, Union[RequestBody, Reference]]] = None
  326. headers: Optional[Dict[str, Union[Header, Reference]]] = None
  327. securitySchemes: Optional[Dict[str, Union[SecurityScheme, Reference]]] = None
  328. links: Optional[Dict[str, Union[Link, Reference]]] = None
  329. # Using Any for Specification Extensions
  330. callbacks: Optional[Dict[str, Union[Dict[str, PathItem], Reference, Any]]] = None
  331. pathItems: Optional[Dict[str, Union[PathItem, Reference]]] = None
  332. class Tag(BaseModelWithConfig):
  333. name: str
  334. description: Optional[str] = None
  335. externalDocs: Optional[ExternalDocumentation] = None
  336. class OpenAPI(BaseModelWithConfig):
  337. openapi: str
  338. info: Info
  339. jsonSchemaDialect: Optional[str] = None
  340. servers: Optional[List[Server]] = None
  341. # Using Any for Specification Extensions
  342. paths: Optional[Dict[str, Union[PathItem, Any]]] = None
  343. webhooks: Optional[Dict[str, Union[PathItem, Reference]]] = None
  344. components: Optional[Components] = None
  345. security: Optional[List[Dict[str, List[str]]]] = None
  346. tags: Optional[List[Tag]] = None
  347. externalDocs: Optional[ExternalDocumentation] = None
  348. _model_rebuild(Schema)
  349. _model_rebuild(Operation)
  350. _model_rebuild(Encoding)