From bd407cc4ed81b21e4287735b7d31a30a452e3a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 30 May 2019 17:40:43 +0400 Subject: [PATCH] :sparkles: Refactor param extraction using Pydantic Field (#278) * :sparkles: Refactor parameter dependency using Pydantic Field * :arrow_up: Upgrade required Pydantic version with latest Shape values * :sparkles: Add tutorials and code for using Enum and Optional * :white_check_mark: Add tests for tutorials with new types and extra cases * :recycle: Format, clean, and add annotations to dependencies.utils * :memo: Update tutorial for query parameters with list defaults * :white_check_mark: Add tests for query param with list default --- docs/img/tutorial/path-params/image03.png | Bin 0 -> 83992 bytes docs/src/path_params/tutorial005.py | 21 ++ docs/src/query_params/tutorial007.py | 11 + .../tutorial012.py | 11 + docs/tutorial/path-params.md | 77 ++++++- docs/tutorial/query-params-str-validations.md | 34 ++- docs/tutorial/query-params.md | 36 +++ fastapi/dependencies/utils.py | 205 +++++++++--------- fastapi/openapi/utils.py | 12 +- pyproject.toml | 2 +- tests/test_invalid_sequence_param.py | 29 +++ .../test_path_params/test_tutorial005.py | 120 ++++++++++ .../test_query_params/test_tutorial007.py | 95 ++++++++ .../test_tutorial012.py | 96 ++++++++ 14 files changed, 623 insertions(+), 126 deletions(-) create mode 100644 docs/img/tutorial/path-params/image03.png create mode 100644 docs/src/path_params/tutorial005.py create mode 100644 docs/src/query_params/tutorial007.py create mode 100644 docs/src/query_params_str_validations/tutorial012.py create mode 100644 tests/test_invalid_sequence_param.py create mode 100644 tests/test_tutorial/test_path_params/test_tutorial005.py create mode 100644 tests/test_tutorial/test_query_params/test_tutorial007.py create mode 100644 tests/test_tutorial/test_query_params_str_validations/test_tutorial012.py diff --git a/docs/img/tutorial/path-params/image03.png b/docs/img/tutorial/path-params/image03.png new file mode 100644 index 0000000000000000000000000000000000000000..d08645d1c8c91da4920d18ca9f190cfe85e8212f GIT binary patch literal 83992 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

!*(w`T zy4aQb$iVX#uD6gzQ2`Xc!e8&S(V-{O%U9|^V+iO#FDly44b#Wnugqx4S*x-<|K-g~ z)MK{cEA+zcKo{lBjiMjR-%@_zfx`#fnk`8Ho`@EdGuJ=3#^rTzN?13=XKl&ffPch; zX4cRnl1!DUMb~oHCQU}7wRTr+jF}D@XM4-z!T$;$yjBFVWkSa!>88z~1&^3wK)?@a zAX>uuq{?)$T|!!*^bstE!M=*k&RiFJ3tw#D^T2&RI}?TZ^pDR?sc_I?xh_QpcWcr& r3FMlX@;w4~0QA5!S*oLx?DBxMo2&>Ij0LX;2W*mJa-t>gbiMx"parsing". ## Data validation @@ -61,12 +61,11 @@ because the path parameter `item_id` had a value of `"foo"`, which is not an `in The same error would appear if you provided a `float` instead of an int, as in: http://127.0.0.1:8000/items/4.2 - !!! check So, with the same Python type declaration, **FastAPI** gives you data validation. - Notice that the error also clearly states exactly the point where the validation didn't pass. - + Notice that the error also clearly states exactly the point where the validation didn't pass. + This is incredibly helpful while developing and debugging code that interacts with your API. ## Documentation @@ -96,8 +95,7 @@ All the data validation is performed under the hood by `Enum`. + +### Create an `Enum` class + +Import `Enum` and create a sub-class that inherits from it. + +And create class attributes with fixed values, those fixed values will be the available valid values: + +```Python hl_lines="1 6 7 8 9" +{!./src/path_params/tutorial005.py!} +``` + +!!! info + Enumerations (or enums) are available in Python since version 3.4. + +!!! tip + If you are wondering, "AlexNet", "ResNet", and "LeNet" are just names of Machine Learning models. + +### Declare a *path parameter* + +Then create a *path parameter* with a type annotation using the enum class you created (`ModelName`): + +```Python hl_lines="16" +{!./src/path_params/tutorial005.py!} +``` + +### Check the docs + +Because the available values for the *path parameter* are specified, the interactive docs can show them nicely: + + + +### Working with Python *enumerations* + +The value of the *path parameter* will be an *enumeration member*. + +#### Compare *enumeration members* + +You can compare it with the *enumeration member* in your created enum `ModelName`: + +```Python hl_lines="17" +{!./src/path_params/tutorial005.py!} +``` + +#### Get the *enumeration value* + +You can get the actual value (a `str` in this case) using `model_name.value`, or in general, `your_enum_member.value`: + +```Python hl_lines="19" +{!./src/path_params/tutorial005.py!} +``` + +!!! tip + You could also access the value `"lenet"` with `ModelName.lenet.value`. + +#### Return *enumeration members* + +You can return *enum members* from your *path operation*, even nested in a JSON body (e.g. a `dict`). + +They will be converted to their corresponding values before returning them to the client: + +```Python hl_lines="18 20 21" +{!./src/path_params/tutorial005.py!} +``` + ## Path parameters containing paths Let's say you have a *path operation* with a path `/files/{file_path}`. diff --git a/docs/tutorial/query-params-str-validations.md b/docs/tutorial/query-params-str-validations.md index a82018437..4258a71fd 100644 --- a/docs/tutorial/query-params-str-validations.md +++ b/docs/tutorial/query-params-str-validations.md @@ -12,7 +12,6 @@ The query parameter `q` is of type `str`, and by default is `None`, so it is opt We are going to enforce that even though `q` is optional, whenever it is provided, it **doesn't exceed a length of 50 characters**. - ### Import `Query` To achieve that, first import `Query` from `fastapi`: @@ -29,7 +28,7 @@ And now use it as the default value of your parameter, setting the parameter `ma {!./src/query_params_str_validations/tutorial002.py!} ``` -As we have to replace the default value `None` with `Query(None)`, the first parameter to `Query` serves the same purpose of defining that default value. +As we have to replace the default value `None` with `Query(None)`, the first parameter to `Query` serves the same purpose of defining that default value. So: @@ -41,7 +40,7 @@ q: str = Query(None) ```Python q: str = None -``` +``` But it declares it explicitly as being a query parameter. @@ -53,7 +52,6 @@ q: str = Query(None, max_length=50) This will validate the data, show a clear error when the data is not valid, and document the parameter in the OpenAPI schema path operation. - ## Add more validations You can also add a parameter `min_length`: @@ -119,7 +117,7 @@ So, when you need to declare a value as required while using `Query`, you can us {!./src/query_params_str_validations/tutorial006.py!} ``` -!!! info +!!! info If you hadn't seen that `...` before: it is a a special single value, it is part of Python and is called "Ellipsis". This will let **FastAPI** know that this parameter is required. @@ -156,11 +154,35 @@ So, the response to that URL would be: !!! tip To declare a query parameter with a type of `list`, like in the example above, you need to explicitly use `Query`, otherwise it would be interpreted as a request body. - The interactive API docs will update accordingly, to allow multiple values: +### Query parameter list / multiple values with defaults + +And you can also define a default `list` of values if none are provided: + +```Python hl_lines="9" +{!./src/query_params_str_validations/tutorial012.py!} +``` + +If you go to: + +``` +http://localhost:8000/items/ +``` + +the default of `q` will be: `["foo", "bar"]` and your response will be: + +```JSON +{ + "q": [ + "foo", + "bar" + ] +} +``` + ## Declare more metadata You can add more information about the parameter. diff --git a/docs/tutorial/query-params.md b/docs/tutorial/query-params.md index 54a71f36d..85a69205d 100644 --- a/docs/tutorial/query-params.md +++ b/docs/tutorial/query-params.md @@ -186,3 +186,39 @@ In this case, there are 3 query parameters: * `needy`, a required `str`. * `skip`, an `int` with a default value of `0`. * `limit`, an optional `int`. + +!!! tip + You could also use `Enum`s the same way as with *path parameters*. + +## Optional type declarations + +!!! warning + This might be an advanced use case. + + You might want to skip it. + +If you are using `mypy` it could complain with type declarations like: + +```Python +limit: int = None +``` + +With an error like: + +``` +Incompatible types in assignment (expression has type "None", variable has type "int") +``` + +In those cases you can use `Optional` to tell `mypy` that the value could be `None`, like: + +```Python +from typing import Optional + +limit: Optional[int] = None +``` + +In a *path operation* that could look like: + +```Python hl_lines="9" +{!./src/query_params/tutorial007.py!} +``` diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index 194187f28..2596d5754 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -1,8 +1,6 @@ import asyncio import inspect from copy import deepcopy -from datetime import date, datetime, time, timedelta -from decimal import Decimal from typing import ( Any, Callable, @@ -14,8 +12,8 @@ from typing import ( Tuple, Type, Union, + cast, ) -from uuid import UUID from fastapi import params from fastapi.dependencies.models import Dependant, SecurityRequirement @@ -23,7 +21,7 @@ from fastapi.security.base import SecurityBase from fastapi.security.oauth2 import OAuth2, SecurityScopes from fastapi.security.open_id_connect_url import OpenIdConnect from fastapi.utils import get_path_param_names -from pydantic import BaseConfig, Schema, create_model +from pydantic import BaseConfig, BaseModel, Schema, create_model from pydantic.error_wrappers import ErrorWrapper from pydantic.errors import MissingError from pydantic.fields import Field, Required, Shape @@ -35,22 +33,21 @@ from starlette.datastructures import FormData, Headers, QueryParams, UploadFile from starlette.requests import Request from starlette.websockets import WebSocket -param_supported_types = ( - str, - int, - float, - bool, - UUID, - date, - datetime, - time, - timedelta, - Decimal, -) - -sequence_shapes = {Shape.LIST, Shape.SET, Shape.TUPLE} +sequence_shapes = { + Shape.LIST, + Shape.SET, + Shape.TUPLE, + Shape.SEQUENCE, + Shape.TUPLE_ELLIPS, +} sequence_types = (list, set, tuple) -sequence_shape_to_type = {Shape.LIST: list, Shape.SET: set, Shape.TUPLE: tuple} +sequence_shape_to_type = { + Shape.LIST: list, + Shape.SET: set, + Shape.TUPLE: tuple, + Shape.SEQUENCE: list, + Shape.TUPLE_ELLIPS: list, +} def get_param_sub_dependant( @@ -126,6 +123,26 @@ def get_flat_dependant(dependant: Dependant) -> Dependant: return flat_dependant +def is_scalar_field(field: Field) -> bool: + return ( + field.shape == Shape.SINGLETON + and not lenient_issubclass(field.type_, BaseModel) + and not isinstance(field.schema, params.Body) + ) + + +def is_scalar_sequence_field(field: Field) -> bool: + if field.shape in sequence_shapes and not lenient_issubclass( + field.type_, BaseModel + ): + if field.sub_fields is not None: + for sub_field in field.sub_fields: + if not is_scalar_field(sub_field): + return False + return True + return False + + def get_dependant( *, path: str, call: Callable, name: str = None, security_scopes: List[str] = None ) -> Dependant: @@ -133,83 +150,78 @@ def get_dependant( endpoint_signature = inspect.signature(call) signature_params = endpoint_signature.parameters dependant = Dependant(call=call, name=name) - for param_name in signature_params: - param = signature_params[param_name] + for param_name, param in signature_params.items(): if isinstance(param.default, params.Depends): sub_dependant = get_param_sub_dependant( param=param, path=path, security_scopes=security_scopes ) dependant.dependencies.append(sub_dependant) - for param_name in signature_params: - param = signature_params[param_name] - if ( - (param.default == param.empty) or isinstance(param.default, params.Path) - ) and (param_name in path_param_names): - assert ( - lenient_issubclass(param.annotation, param_supported_types) - or param.annotation == param.empty + for param_name, param in signature_params.items(): + if isinstance(param.default, params.Depends): + continue + if add_non_field_param_to_dependency(param=param, dependant=dependant): + continue + param_field = get_param_field(param=param, default_schema=params.Query) + if param_name in path_param_names: + assert param.default == param.empty or isinstance( + param.default, params.Path + ), "Path params must have no defaults or use Path(...)" + assert is_scalar_field( + field=param_field ), f"Path params must be of one of the supported types" - add_param_to_fields( + param_field = get_param_field( param=param, - dependant=dependant, default_schema=params.Path, force_type=params.ParamTypes.path, ) - elif ( - param.default == param.empty - or param.default is None - or isinstance(param.default, param_supported_types) - ) and ( - param.annotation == param.empty - or lenient_issubclass(param.annotation, param_supported_types) - ): - add_param_to_fields( - param=param, dependant=dependant, default_schema=params.Query - ) - elif isinstance(param.default, params.Param): - if param.annotation != param.empty: - origin = getattr(param.annotation, "__origin__", None) - param_all_types = param_supported_types + (list, tuple, set) - if isinstance(param.default, (params.Query, params.Header)): - assert lenient_issubclass( - param.annotation, param_all_types - ) or lenient_issubclass( - origin, param_all_types - ), f"Parameters for Query and Header must be of type str, int, float, bool, list, tuple or set: {param}" - else: - assert lenient_issubclass( - param.annotation, param_supported_types - ), f"Parameters for Path and Cookies must be of type str, int, float, bool: {param}" - add_param_to_fields( - param=param, dependant=dependant, default_schema=params.Query - ) - elif lenient_issubclass(param.annotation, Request): - dependant.request_param_name = param_name - elif lenient_issubclass(param.annotation, WebSocket): - dependant.websocket_param_name = param_name - elif lenient_issubclass(param.annotation, BackgroundTasks): - dependant.background_tasks_param_name = param_name - elif lenient_issubclass(param.annotation, SecurityScopes): - dependant.security_scopes_param_name = param_name - elif not isinstance(param.default, params.Depends): - add_param_to_body_fields(param=param, dependant=dependant) + add_param_to_fields(field=param_field, dependant=dependant) + elif is_scalar_field(field=param_field): + add_param_to_fields(field=param_field, dependant=dependant) + elif isinstance( + param.default, (params.Query, params.Header) + ) and is_scalar_sequence_field(param_field): + add_param_to_fields(field=param_field, dependant=dependant) + else: + assert isinstance( + param_field.schema, params.Body + ), f"Param: {param_field.name} can only be a request body, using Body(...)" + dependant.body_params.append(param_field) return dependant -def add_param_to_fields( +def add_non_field_param_to_dependency( + *, param: inspect.Parameter, dependant: Dependant +) -> Optional[bool]: + if lenient_issubclass(param.annotation, Request): + dependant.request_param_name = param.name + return True + elif lenient_issubclass(param.annotation, WebSocket): + dependant.websocket_param_name = param.name + return True + elif lenient_issubclass(param.annotation, BackgroundTasks): + dependant.background_tasks_param_name = param.name + return True + elif lenient_issubclass(param.annotation, SecurityScopes): + dependant.security_scopes_param_name = param.name + return True + return None + + +def get_param_field( *, param: inspect.Parameter, - dependant: Dependant, - default_schema: Type[Schema] = params.Param, + default_schema: Type[params.Param] = params.Param, force_type: params.ParamTypes = None, -) -> None: +) -> Field: default_value = Required + had_schema = False if not param.default == param.empty: default_value = param.default - if isinstance(default_value, params.Param): + if isinstance(default_value, Schema): + had_schema = True schema = default_value default_value = schema.default - if getattr(schema, "in_", None) is None: + if isinstance(schema, params.Param) and getattr(schema, "in_", None) is None: schema.in_ = default_schema.in_ if force_type: schema.in_ = force_type @@ -234,43 +246,26 @@ def add_param_to_fields( class_validators={}, schema=schema, ) - if schema.in_ == params.ParamTypes.path: + if not had_schema and not is_scalar_field(field=field): + field.schema = params.Body(schema.default) + return field + + +def add_param_to_fields(*, field: Field, dependant: Dependant) -> None: + field.schema = cast(params.Param, field.schema) + if field.schema.in_ == params.ParamTypes.path: dependant.path_params.append(field) - elif schema.in_ == params.ParamTypes.query: + elif field.schema.in_ == params.ParamTypes.query: dependant.query_params.append(field) - elif schema.in_ == params.ParamTypes.header: + elif field.schema.in_ == params.ParamTypes.header: dependant.header_params.append(field) else: assert ( - schema.in_ == params.ParamTypes.cookie - ), f"non-body parameters must be in path, query, header or cookie: {param.name}" + field.schema.in_ == params.ParamTypes.cookie + ), f"non-body parameters must be in path, query, header or cookie: {field.name}" dependant.cookie_params.append(field) -def add_param_to_body_fields(*, param: inspect.Parameter, dependant: Dependant) -> None: - default_value = Required - if not param.default == param.empty: - default_value = param.default - if isinstance(default_value, Schema): - schema = default_value - default_value = schema.default - else: - schema = Schema(default_value) - required = default_value == Required - annotation = get_annotation_from_schema(param.annotation, schema) - field = Field( - name=param.name, - type_=annotation, - default=None if required else default_value, - alias=schema.alias or param.name, - required=required, - model_config=BaseConfig, - class_validators={}, - schema=schema, - ) - dependant.body_params.append(field) - - def is_coroutine_callable(call: Callable) -> bool: if inspect.isfunction(call): return asyncio.iscoroutinefunction(call) @@ -354,7 +349,7 @@ def request_params_to_args( if field.shape in sequence_shapes and isinstance( received_params, (QueryParams, Headers) ): - value = received_params.getlist(field.alias) + value = received_params.getlist(field.alias) or field.default else: value = received_params.get(field.alias) schema: params.Param = field.schema diff --git a/fastapi/openapi/utils.py b/fastapi/openapi/utils.py index 87e223cb6..26d491bea 100644 --- a/fastapi/openapi/utils.py +++ b/fastapi/openapi/utils.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional, Sequence, Tuple, Type +from typing import Any, Dict, List, Optional, Sequence, Tuple, Type, cast from fastapi import routing from fastapi.dependencies.models import Dependant @@ -9,7 +9,7 @@ from fastapi.openapi.models import OpenAPI from fastapi.params import Body, Param from fastapi.utils import get_flat_models_from_routes, get_model_definitions from pydantic.fields import Field -from pydantic.schema import Schema, field_schema, get_model_name_map +from pydantic.schema import field_schema, get_model_name_map from pydantic.utils import lenient_issubclass from starlette.responses import JSONResponse from starlette.routing import BaseRoute @@ -97,12 +97,8 @@ def get_openapi_operation_request_body( body_schema, _ = field_schema( body_field, model_name_map=model_name_map, ref_prefix=REF_PREFIX ) - schema: Schema = body_field.schema - if isinstance(schema, Body): - request_media_type = schema.media_type - else: - # Includes not declared media types (Schema) - request_media_type = "application/json" + body_field.schema = cast(Body, body_field.schema) + request_media_type = body_field.schema.media_type required = body_field.required request_body_oai: Dict[str, Any] = {} if required: diff --git a/pyproject.toml b/pyproject.toml index e8be41838..8700b0ef3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ classifiers = [ ] requires = [ "starlette >=0.11.1,<=0.12.0", - "pydantic >=0.17,<=0.26.0" + "pydantic >=0.26,<=0.26.0" ] description-file = "README.md" requires-python = ">=3.6" diff --git a/tests/test_invalid_sequence_param.py b/tests/test_invalid_sequence_param.py new file mode 100644 index 000000000..bdc4b1bcb --- /dev/null +++ b/tests/test_invalid_sequence_param.py @@ -0,0 +1,29 @@ +from typing import List, Tuple + +import pytest +from fastapi import FastAPI, Query +from pydantic import BaseModel + + +def test_invalid_sequence(): + with pytest.raises(AssertionError): + app = FastAPI() + + class Item(BaseModel): + title: str + + @app.get("/items/") + def read_items(q: List[Item] = Query(None)): + pass # pragma: no cover + + +def test_invalid_tuple(): + with pytest.raises(AssertionError): + app = FastAPI() + + class Item(BaseModel): + title: str + + @app.get("/items/") + def read_items(q: Tuple[Item, Item] = Query(None)): + pass # pragma: no cover diff --git a/tests/test_tutorial/test_path_params/test_tutorial005.py b/tests/test_tutorial/test_path_params/test_tutorial005.py new file mode 100644 index 000000000..3245cdceb --- /dev/null +++ b/tests/test_tutorial/test_path_params/test_tutorial005.py @@ -0,0 +1,120 @@ +import pytest +from starlette.testclient import TestClient + +from path_params.tutorial005 import app + +client = TestClient(app) + +openapi_schema = { + "openapi": "3.0.2", + "info": {"title": "Fast API", "version": "0.1.0"}, + "paths": { + "/model/{model_name}": { + "get": { + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + "summary": "Get Model", + "operationId": "get_model_model__model_name__get", + "parameters": [ + { + "required": True, + "schema": { + "title": "Model_Name", + "enum": ["alexnet", "resnet", "lenet"], + }, + "name": "model_name", + "in": "path", + } + ], + } + } + }, + "components": { + "schemas": { + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": {"type": "string"}, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": {"$ref": "#/components/schemas/ValidationError"}, + } + }, + }, + } + }, +} + + +def test_openapi(): + response = client.get("/openapi.json") + assert response.status_code == 200 + assert response.json() == openapi_schema + + +@pytest.mark.parametrize( + "url,status_code,expected", + [ + ( + "/model/alexnet", + 200, + {"model_name": "alexnet", "message": "Deep Learning FTW!"}, + ), + ( + "/model/lenet", + 200, + {"model_name": "lenet", "message": "LeCNN all the images"}, + ), + ( + "/model/resnet", + 200, + {"model_name": "resnet", "message": "Have some residuals"}, + ), + ( + "/model/foo", + 422, + { + "detail": [ + { + "loc": ["path", "model_name"], + "msg": "value is not a valid enumeration member", + "type": "type_error.enum", + } + ] + }, + ), + ], +) +def test_get_enums(url, status_code, expected): + response = client.get(url) + assert response.status_code == status_code + assert response.json() == expected diff --git a/tests/test_tutorial/test_query_params/test_tutorial007.py b/tests/test_tutorial/test_query_params/test_tutorial007.py new file mode 100644 index 000000000..a0fb23850 --- /dev/null +++ b/tests/test_tutorial/test_query_params/test_tutorial007.py @@ -0,0 +1,95 @@ +from starlette.testclient import TestClient + +from query_params.tutorial007 import app + +client = TestClient(app) + +openapi_schema = { + "openapi": "3.0.2", + "info": {"title": "Fast API", "version": "0.1.0"}, + "paths": { + "/items/{item_id}": { + "get": { + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + "summary": "Read User Item", + "operationId": "read_user_item_items__item_id__get", + "parameters": [ + { + "required": True, + "schema": {"title": "Item_Id", "type": "string"}, + "name": "item_id", + "in": "path", + }, + { + "required": False, + "schema": {"title": "Limit", "type": "integer"}, + "name": "limit", + "in": "query", + }, + ], + } + } + }, + "components": { + "schemas": { + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": {"type": "string"}, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": {"$ref": "#/components/schemas/ValidationError"}, + } + }, + }, + } + }, +} + + +def test_openapi(): + response = client.get("/openapi.json") + assert response.status_code == 200 + assert response.json() == openapi_schema + + +def test_read_item(): + response = client.get("/items/foo") + assert response.status_code == 200 + assert response.json() == {"item_id": "foo", "limit": None} + + +def test_read_item_query(): + response = client.get("/items/foo?limit=5") + assert response.status_code == 200 + assert response.json() == {"item_id": "foo", "limit": 5} diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial012.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial012.py new file mode 100644 index 000000000..1e00c5017 --- /dev/null +++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial012.py @@ -0,0 +1,96 @@ +from starlette.testclient import TestClient + +from query_params_str_validations.tutorial012 import app + +client = TestClient(app) + +openapi_schema = { + "openapi": "3.0.2", + "info": {"title": "Fast API", "version": "0.1.0"}, + "paths": { + "/items/": { + "get": { + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + "summary": "Read Items", + "operationId": "read_items_items__get", + "parameters": [ + { + "required": False, + "schema": { + "title": "Q", + "type": "array", + "items": {"type": "string"}, + "default": ["foo", "bar"], + }, + "name": "q", + "in": "query", + } + ], + } + } + }, + "components": { + "schemas": { + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": {"type": "string"}, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": {"$ref": "#/components/schemas/ValidationError"}, + } + }, + }, + } + }, +} + + +def test_openapi_schema(): + response = client.get("/openapi.json") + assert response.status_code == 200 + assert response.json() == openapi_schema + + +def test_default_query_values(): + url = "/items/" + response = client.get(url) + assert response.status_code == 200 + assert response.json() == {"q": ["foo", "bar"]} + + +def test_multi_query_values(): + url = "/items/?q=baz&q=foobar" + response = client.get(url) + assert response.status_code == 200 + assert response.json() == {"q": ["baz", "foobar"]}