Issue 1:
First Check

I added a very descriptive title to this issue.

I used the GitHub search to find a similar issue and didn't find it.

I searched the FastAPI documentation, with the integrated search.

I already searched in Google "How to X in FastAPI" and didn't find any information.

I already read and followed all the tutorial in the docs and didn't find an answer.

I already checked if it is not related to FastAPI but to Pydantic.

I already checked if it is not related to FastAPI but to Swagger UI.

I already checked if it is not related to FastAPI but to ReDoc.

Commit to Help

I commit to help with one of those options 👆

Example Code

class ControlValue(BaseModel):
"""V4l2 control value model"""

value: Union[int, str, List[int], TriggerSequenceValue, Any]
""": value of v4l2 control"""

class FrameSize(BaseModel):
"""Frame size model"""

width: int
height: int

class Config:
"""Config class"""

fields = {"width": {"example": 1024}, "height": {"example": 1024}}

class FrameResolution(FrameSize):
"""Video device frame resolution model"""

width: Optional[int] = None
height: Optional[int] = None

class CropRect(FrameSize):
"""Video device frame crop rectangle model"""

left: int = Field(default=..., example=0)
top: int = Field(default=..., example=0)

class ControlExamples:
"""Examples for controls API"""

name = {
"exposure": {
"summary": "Exposure Time",
"value": "Exposure Time, Absolute",
},
"gain": {"summary": "Gain", "value": "Gain"},
"red": {"summary": "Red Gain", "value": "Red Balance"},
"green": {"summary": "Green Gain", "value": "Green Balance"},
"blue": {"summary": "Blue Gain", "value": "Blue Balance"},
"fpn": {
"summary": "Fixed Pattern Noise Correction",
"value": "Fixed Pattern Noise Correction",
},
}

value = {
"exposure": {
"summary": "Exposure Time",
"value": {"value": 5000},
},
"gain": {"summary": "Gain", "value": {"value": 0}},
"red": {"summary": "Red Gain", "value": {"value": 16384}},
"green": {"summary": "Green Gain", "value": {"value": 16384}},
"blue": {"summary": "Blue Gain", "value": {"value": 16384}},
"array": {"summary": "Array Control", "value": {"value": [1, 2, 3]}},
}

get_control_responses = {
200: {
"description": "Success",
"content": {
"application/json": {
"examples": {
**ControlExamples.value,
"sensor type": {
"summary": "Sensor Type",
"value": {"value": "CMV4000v3 Bayer"},
},
}
}
},
},
}

@app.get(
V4L2_ROUTE + V4L2Endpoints.control + "/{ctrl_id_or_name}",
response_model=ControlValue,
responses=get_control_responses,
tags=[Tags.controls],
)
def get_control(
ctrl_id_or_name: Union[int, str] = Path( # noqa: B008
description="Id or Name of v4l2 control",
examples={
**ControlExamples.name,
"sensor type": {
"summary": "Sensor Type",
"value": "Sensor Type",
},
},
),
params: CommonParams = Depends(), # noqa: B008
) -> JSONResponse:
"""
Get value of single v4l2 control

Supports both normal and extended controls
"""
return check_exceptions(Camera.get_control, ctrl_id_or_name, params.dev)

set_control_responses = {
200: {
"description": "Success",
"content": {"application/json": {"examples": ControlExamples.value}},
},
}

@app.post(
V4L2_ROUTE + V4L2Endpoints.control + "/{ctrl_id_or_name}",
response_model=ControlValue,
responses=set_control_responses,
tags=[Tags.controls],
)
def set_control(
ctrl_id_or_name: Union[int, str] = Path( # noqa: B008
description="Id or Name of v4l2 control",
examples=ControlExamples.name,
),
value: ControlValue = Body( # noqa: B008
examples=ControlExamples.value, descripton="v4l2 control value"
),
params: CommonParams = Depends(), # noqa: B008
) -> JSONResponse:
"""
Set value of single v4l2 control

Supports both normal and extended controls
"""
return check_exceptions(Camera.set_control, ctrl_id_or_name, value, params.dev)

...

client = TestClient(app)

class TestControl:
"""Tests for 'control' endpoint"""

base_route = V4L2_ROUTE + V4L2Endpoints.control

def test_get_control(self):
control_name = "exposure time, absolute"
response = client.get(self.base_route + "/" + control_name)
assert response.status_code == 200
parse_obj_as(ControlValue, response.json())

def test_get_control_bad(self):
response = client.get(self.base_route + "/bla")
assert response.status_code == 422

def test_set_control(self):
control_name = "exposure time, absolute"
control_value = 5000
print(ControlValue.schema_json())
json_value = jsonable_encoder(ControlValue(value=control_value))
response = client.post(
self.base_route + "/" + control_name,
json=json_value,
)
assert response.status_code == 200
assert response.json() == json_value

Description

I have made an API in which I defined examples for pretty much everything: in the BaseModels, responses, Query, ...

This has made interactive testing with swagger /docs pretty nice.

However now I wan't to move to proper testing with pytest.

I have found this: https://fastapi.tiangolo.com/tutorial/testing/ but I can't find information on how to make the testing more "automatic".

For example, I would like to be able to re-use the examples defined in the API instead of duplicating them in the test files.

And as my API interacts with HW (a v4l2 camera) I can't necessary check that the response content is right because I don't know it before hand.

But it could be relevant to check that the type is right (BaseModel)?

Or is that overkill because of all the internal validation?

So far I came up with: parse_obj_as(ControlValue, response.json())

Operating System

Linux

Operating System Details

No response

FastAPI Version

0.85.0

Python Version

Python 3.10.5

Additional Context

No response

Issue 2:
First Check

I added a very descriptive title to this issue.

I used the GitHub search to find a similar issue and didn't find it.

I searched the FastAPI documentation, with the integrated search.

I already searched in Google "How to X in FastAPI" and didn't find any information.

I already read and followed all the tutorial in the docs and didn't find an answer.

I already checked if it is not related to FastAPI but to Pydantic.

I already checked if it is not related to FastAPI but to Swagger UI.

I already checked if it is not related to FastAPI but to ReDoc.

Commit to Help

I commit to help with one of those options 👆

Example Code

import json
from fastapi import FastAPI, Header, Request
from fastapi.openapi.utils import get_openapi

app = FastAPI()

@app.get("/items/")
async def read_items(x_tenant_id: str = Header()):
return [{"hello": "world"}]

@app.get("/users")
async def read_users(request: Request):
return [{"tag": "@jayashankarjayan"}]

def custom_openapi():
header = {
"required": True,
"schema": {
"title": "X-Custom-Id",
"type": "string"
},
"name": "x-custom-id",
"in": "header"
}
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
title="Custom title",
version="2.5.0",
description="This is a very custom OpenAPI schema",
routes=app.routes,
)
openapi_schema["info"]["x-logo"] = {
"url": "https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png"
}
app.openapi_schema = openapi_schema

paths = openapi_schema["paths"]
for url, methods in paths.items():
for method in methods:
if methods[method].get("parameters"):
methods[method]["parameters"].append(header)
else:
methods[method]["parameters"] = [header]

return app.openapi_schema

app.openapi = custom_openapi

Description

I have a requirement in my application where all my APIs spread across multiple routers are required to have custom headers, eg: x-custom-header. I want these headers to be visible as fields on the Swagger UI as well so that a user who accesses this Swagger UI can use the API endpoints by providing valid values to the headers.

I have two options -

I add the header as an argument to every single route individually. This introduces a lot of duplication as I have to repeat the same arguments over multiple routes.

I modify the OpenAPI Schema (as seen in the above code snippet) and add the header across all routes at one go. While this works, it seems more like a somewhat ugly hack.

I would like to know that is there a better, elegant option than the aforementioned one which I can use to get header fields in my Swagger UI while avoiding duplication in my code and keeping it elegant.

All help is appreciated. Thanks!

Operating System

Linux, Windows, macOS

Operating System Details

No response

FastAPI Version

0.86.0

Python Version

3.8.9

Additional Context

This is how I would like the custom header to appear without actually mentioning it explicitly on the route itself (the result seen in the screenshot can be replicated using the code snippet shared above)

Kindly note that there could be multiple such custom headers that I want to see on my Swagger UI