fastapi/fastapi/openapi/models.py

436 lines
14 KiB
Python

from collections.abc import Callable, Iterable, Mapping
from enum import Enum
from typing import Annotated, Any, Literal, Optional, Union
from fastapi._compat import with_info_plain_validator_function
from fastapi.logger import logger
from pydantic import (
AnyUrl,
BaseModel,
Field,
GetJsonSchemaHandler,
)
from typing_extensions import TypedDict
from typing_extensions import deprecated as typing_deprecated
try:
import email_validator
assert email_validator # make autoflake ignore the unused import
from pydantic import EmailStr
except ImportError: # pragma: no cover
class EmailStr(str): # type: ignore
@classmethod
def __get_validators__(cls) -> Iterable[Callable[..., Any]]:
yield cls.validate
@classmethod
def validate(cls, v: Any) -> str:
logger.warning(
"email-validator not installed, email fields will be treated as str.\n"
"To install, run: pip install email-validator"
)
return str(v)
@classmethod
def _validate(cls, __input_value: Any, _: Any) -> str:
logger.warning(
"email-validator not installed, email fields will be treated as str.\n"
"To install, run: pip install email-validator"
)
return str(__input_value)
@classmethod
def __get_pydantic_json_schema__(
cls, core_schema: Mapping[str, Any], handler: GetJsonSchemaHandler
) -> dict[str, Any]:
return {"type": "string", "format": "email"}
@classmethod
def __get_pydantic_core_schema__(
cls, source: type[Any], handler: Callable[[Any], Mapping[str, Any]]
) -> Mapping[str, Any]:
return with_info_plain_validator_function(cls._validate)
class BaseModelWithConfig(BaseModel):
model_config = {"extra": "allow"}
class Contact(BaseModelWithConfig):
name: str | None = None
url: AnyUrl | None = None
email: EmailStr | None = None
class License(BaseModelWithConfig):
name: str
identifier: str | None = None
url: AnyUrl | None = None
class Info(BaseModelWithConfig):
title: str
summary: str | None = None
description: str | None = None
termsOfService: str | None = None
contact: Contact | None = None
license: License | None = None
version: str
class ServerVariable(BaseModelWithConfig):
enum: Annotated[list[str] | None, Field(min_length=1)] = None
default: str
description: str | None = None
class Server(BaseModelWithConfig):
url: AnyUrl | str
description: str | None = None
variables: dict[str, ServerVariable] | None = None
class Reference(BaseModel):
ref: str = Field(alias="$ref")
class Discriminator(BaseModel):
propertyName: str
mapping: dict[str, str] | None = None
class XML(BaseModelWithConfig):
name: str | None = None
namespace: str | None = None
prefix: str | None = None
attribute: bool | None = None
wrapped: bool | None = None
class ExternalDocumentation(BaseModelWithConfig):
description: str | None = None
url: AnyUrl
# Ref JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation#name-type
SchemaType = Literal[
"array", "boolean", "integer", "null", "number", "object", "string"
]
class Schema(BaseModelWithConfig):
# Ref: JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-core.html#name-the-json-schema-core-vocabu
# Core Vocabulary
schema_: str | None = Field(default=None, alias="$schema")
vocabulary: str | None = Field(default=None, alias="$vocabulary")
id: str | None = Field(default=None, alias="$id")
anchor: str | None = Field(default=None, alias="$anchor")
dynamicAnchor: str | None = Field(default=None, alias="$dynamicAnchor")
ref: str | None = Field(default=None, alias="$ref")
dynamicRef: str | None = Field(default=None, alias="$dynamicRef")
defs: dict[str, "SchemaOrBool"] | None = Field(default=None, alias="$defs")
comment: str | None = Field(default=None, alias="$comment")
# Ref: JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-core.html#name-a-vocabulary-for-applying-s
# A Vocabulary for Applying Subschemas
allOf: list["SchemaOrBool"] | None = None
anyOf: list["SchemaOrBool"] | None = None
oneOf: list["SchemaOrBool"] | None = None
not_: Optional["SchemaOrBool"] = Field(default=None, alias="not")
if_: Optional["SchemaOrBool"] = Field(default=None, alias="if")
then: Optional["SchemaOrBool"] = None
else_: Optional["SchemaOrBool"] = Field(default=None, alias="else")
dependentSchemas: dict[str, "SchemaOrBool"] | None = None
prefixItems: list["SchemaOrBool"] | None = None
items: Optional["SchemaOrBool"] = None
contains: Optional["SchemaOrBool"] = None
properties: dict[str, "SchemaOrBool"] | None = None
patternProperties: dict[str, "SchemaOrBool"] | None = None
additionalProperties: Optional["SchemaOrBool"] = None
propertyNames: Optional["SchemaOrBool"] = None
unevaluatedItems: Optional["SchemaOrBool"] = None
unevaluatedProperties: Optional["SchemaOrBool"] = None
# Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-structural
# A Vocabulary for Structural Validation
type: SchemaType | list[SchemaType] | None = None
enum: list[Any] | None = None
const: Any | None = None
multipleOf: float | None = Field(default=None, gt=0)
maximum: float | None = None
exclusiveMaximum: float | None = None
minimum: float | None = None
exclusiveMinimum: float | None = None
maxLength: int | None = Field(default=None, ge=0)
minLength: int | None = Field(default=None, ge=0)
pattern: str | None = None
maxItems: int | None = Field(default=None, ge=0)
minItems: int | None = Field(default=None, ge=0)
uniqueItems: bool | None = None
maxContains: int | None = Field(default=None, ge=0)
minContains: int | None = Field(default=None, ge=0)
maxProperties: int | None = Field(default=None, ge=0)
minProperties: int | None = Field(default=None, ge=0)
required: list[str] | None = None
dependentRequired: dict[str, set[str]] | None = None
# Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-vocabularies-for-semantic-c
# Vocabularies for Semantic Content With "format"
format: str | None = None
# Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-the-conten
# A Vocabulary for the Contents of String-Encoded Data
contentEncoding: str | None = None
contentMediaType: str | None = None
contentSchema: Optional["SchemaOrBool"] = None
# Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-basic-meta
# A Vocabulary for Basic Meta-Data Annotations
title: str | None = None
description: str | None = None
default: Any | None = None
deprecated: bool | None = None
readOnly: bool | None = None
writeOnly: bool | None = None
examples: list[Any] | None = None
# Ref: OpenAPI 3.1.0: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#schema-object
# Schema Object
discriminator: Discriminator | None = None
xml: XML | None = None
externalDocs: ExternalDocumentation | None = None
example: Annotated[
Any | None,
typing_deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead."
),
] = None
# Ref: https://json-schema.org/draft/2020-12/json-schema-core.html#name-json-schema-documents
# A JSON Schema MUST be an object or a boolean.
SchemaOrBool = Schema | bool
class Example(TypedDict, total=False):
summary: str | None
description: str | None
value: Any | None
externalValue: AnyUrl | None
__pydantic_config__ = {"extra": "allow"} # type: ignore[misc]
class ParameterInType(Enum):
query = "query"
header = "header"
path = "path"
cookie = "cookie"
class Encoding(BaseModelWithConfig):
contentType: str | None = None
headers: dict[str, Union["Header", Reference]] | None = None
style: str | None = None
explode: bool | None = None
allowReserved: bool | None = None
class MediaType(BaseModelWithConfig):
schema_: Schema | Reference | None = Field(default=None, alias="schema")
example: Any | None = None
examples: dict[str, Example | Reference] | None = None
encoding: dict[str, Encoding] | None = None
class ParameterBase(BaseModelWithConfig):
description: str | None = None
required: bool | None = None
deprecated: bool | None = None
# Serialization rules for simple scenarios
style: str | None = None
explode: bool | None = None
allowReserved: bool | None = None
schema_: Schema | Reference | None = Field(default=None, alias="schema")
example: Any | None = None
examples: dict[str, Example | Reference] | None = None
# Serialization rules for more complex scenarios
content: dict[str, MediaType] | None = None
class Parameter(ParameterBase):
name: str
in_: ParameterInType = Field(alias="in")
class Header(ParameterBase):
pass
class RequestBody(BaseModelWithConfig):
description: str | None = None
content: dict[str, MediaType]
required: bool | None = None
class Link(BaseModelWithConfig):
operationRef: str | None = None
operationId: str | None = None
parameters: dict[str, Any | str] | None = None
requestBody: Any | str | None = None
description: str | None = None
server: Server | None = None
class Response(BaseModelWithConfig):
description: str
headers: dict[str, Header | Reference] | None = None
content: dict[str, MediaType] | None = None
links: dict[str, Link | Reference] | None = None
class Operation(BaseModelWithConfig):
tags: list[str] | None = None
summary: str | None = None
description: str | None = None
externalDocs: ExternalDocumentation | None = None
operationId: str | None = None
parameters: list[Parameter | Reference] | None = None
requestBody: RequestBody | Reference | None = None
# Using Any for Specification Extensions
responses: dict[str, Response | Any] | None = None
callbacks: dict[str, dict[str, "PathItem"] | Reference] | None = None
deprecated: bool | None = None
security: list[dict[str, list[str]]] | None = None
servers: list[Server] | None = None
class PathItem(BaseModelWithConfig):
ref: str | None = Field(default=None, alias="$ref")
summary: str | None = None
description: str | None = None
get: Operation | None = None
put: Operation | None = None
post: Operation | None = None
delete: Operation | None = None
options: Operation | None = None
head: Operation | None = None
patch: Operation | None = None
trace: Operation | None = None
servers: list[Server] | None = None
parameters: list[Parameter | Reference] | None = None
class SecuritySchemeType(Enum):
apiKey = "apiKey"
http = "http"
oauth2 = "oauth2"
openIdConnect = "openIdConnect"
class SecurityBase(BaseModelWithConfig):
type_: SecuritySchemeType = Field(alias="type")
description: str | None = None
class APIKeyIn(Enum):
query = "query"
header = "header"
cookie = "cookie"
class APIKey(SecurityBase):
type_: SecuritySchemeType = Field(default=SecuritySchemeType.apiKey, alias="type")
in_: APIKeyIn = Field(alias="in")
name: str
class HTTPBase(SecurityBase):
type_: SecuritySchemeType = Field(default=SecuritySchemeType.http, alias="type")
scheme: str
class HTTPBearer(HTTPBase):
scheme: Literal["bearer"] = "bearer"
bearerFormat: str | None = None
class OAuthFlow(BaseModelWithConfig):
refreshUrl: str | None = None
scopes: dict[str, str] = {}
class OAuthFlowImplicit(OAuthFlow):
authorizationUrl: str
class OAuthFlowPassword(OAuthFlow):
tokenUrl: str
class OAuthFlowClientCredentials(OAuthFlow):
tokenUrl: str
class OAuthFlowAuthorizationCode(OAuthFlow):
authorizationUrl: str
tokenUrl: str
class OAuthFlows(BaseModelWithConfig):
implicit: OAuthFlowImplicit | None = None
password: OAuthFlowPassword | None = None
clientCredentials: OAuthFlowClientCredentials | None = None
authorizationCode: OAuthFlowAuthorizationCode | None = None
class OAuth2(SecurityBase):
type_: SecuritySchemeType = Field(default=SecuritySchemeType.oauth2, alias="type")
flows: OAuthFlows
class OpenIdConnect(SecurityBase):
type_: SecuritySchemeType = Field(
default=SecuritySchemeType.openIdConnect, alias="type"
)
openIdConnectUrl: str
SecurityScheme = APIKey | HTTPBase | OAuth2 | OpenIdConnect | HTTPBearer
class Components(BaseModelWithConfig):
schemas: dict[str, Schema | Reference] | None = None
responses: dict[str, Response | Reference] | None = None
parameters: dict[str, Parameter | Reference] | None = None
examples: dict[str, Example | Reference] | None = None
requestBodies: dict[str, RequestBody | Reference] | None = None
headers: dict[str, Header | Reference] | None = None
securitySchemes: dict[str, SecurityScheme | Reference] | None = None
links: dict[str, Link | Reference] | None = None
# Using Any for Specification Extensions
callbacks: dict[str, dict[str, PathItem] | Reference | Any] | None = None
pathItems: dict[str, PathItem | Reference] | None = None
class Tag(BaseModelWithConfig):
name: str
description: str | None = None
externalDocs: ExternalDocumentation | None = None
class OpenAPI(BaseModelWithConfig):
openapi: str
info: Info
jsonSchemaDialect: str | None = None
servers: list[Server] | None = None
# Using Any for Specification Extensions
paths: dict[str, PathItem | Any] | None = None
webhooks: dict[str, PathItem | Reference] | None = None
components: Components | None = None
security: list[dict[str, list[str]]] | None = None
tags: list[Tag] | None = None
externalDocs: ExternalDocumentation | None = None
Schema.model_rebuild()
Operation.model_rebuild()
Encoding.model_rebuild()