Is there any way to used named method parameter in Python - corresponding to this Java example:
#ApiMethod(
name = "foos.remove",
path = "foos/{id}",
httpMethod = HttpMethod.DELETE,
)
public void removeFoo(#Named("id") String id) {
}
In my Python version, if I set the #endpoints.method path to foos/{id} the URL gets matched correctly, but how do I access the parameter?
There is not strict equivalent, but if {id} is in your path, then there must be a field called id in the protorpc message class you use for the request class in the method.
For example:
from google.appengine.ext import endpoints
from protorpc import messages
from protorpc import remote
class MyMessageClass(messages.Message):
id = messages.StringField(1) # Or any other field type
#endpoints.api(...)
class MyApi(remote.Service):
#endpoints.method(MyMessageClass, SomeResponseClass,
..., path='foos/{id}')
def my_method(self, request):
...
Related
I would like to achieve the following :
My application contains some "sub-domains" which correspond to different parts of the app.
Each domain has its own entities
I would like to write a single controler like so :
#app.get("/{domain}/entity/{entity}/{id}")
async def read_users(domain: Domain, entity: Entity, id: Int):
pass
considering Entity would be an Enum that could change following the selected domain.
For instance, if the domain is "architecture", Entity could be defined like :
class Entity(str, Enum):
building = "building"
floor = "floor"
but if the selected domain is "vehicle", the matching Entity would be :
class Entity(str, Enum):
engine = "engine"
wheels = "wheels"
More generally, I guess what I'm looking for is a way to make a path parameter validation dependent on another path parameter.
This way :
GET /architecture/entity/floor/1 is valid since, floor is a valid entity for domain architecture
GET /vehicle/entity/wheels/5 is valid since, wheels is a valid entity for domain vehicle
GET /architecture/entity/engine/1 is invalid since, engine is not a valid entity for domain architecture
Is there any way to achieve this ?
You can use closures. The following code does not use Enums for brevity :
from fastapi import FastAPI
app = FastAPI()
domains = {"architecture": ["building","floor"], "vehicle": ["engine","wheels"]}
def set_route(domain,entity):
url = "/{}/entity/{}/{{id}}".format(domain,entity)
#app.get(url)
async def read_users(id: int):
return(f"Users of the {domain} {entity} #{id}")
for domain, entities in domains.items():
for entity in entities:
set_route(domain,entity)
And it yields the desired API schema :
I'm not sure if exactly what you want is possible, for sure you can write controller where you add pydantic validation, and handle exception if it throws validation error:
from pydantic import BaseModel, ValidationError
from enum import Enumfrom fastapi import FastAPI, Request, status
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse
from typing import Union, Literal
class ArchitectureEntity(BaseModel):
entity: Union[Literal['building'], Literal['floor']]
class VehicleEntity(BaseModel):
entity: Union[Literal['wheels'], Literal['engine']]
#app.exception_handler(ValidationError)
async def validation_exception_handler(request: Request, exc: ValidationError):
return JSONResponse(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
content=jsonable_encoder({"detail": exc.errors(), "Error": "Entity not permitted"}),
)
#app.get("/{domain}/entity/{entity}/{id}")
async def read_users(domain: Domain, entity: Entity, id: int):
if domain == 'architecture':
entity = ArchitectureEntity(entity=entity)
elif domain == 'vehicle':
entity = VehicleEntity(entity=entity)
return {'architecture': entity}
However openapi docs will not show, that e.g architecture and engine are not allowed together.
I'm handling this request in my code (Python3.9, FastAPI, Pydantic):
https://myapi.com/api?params[A]=1¶ms[B]=2
I tried to make following model:
BaseModel for handling special get request
(for fastapi.Query and pydantic.Field is same)
I also set up aliases for it, but in swagger docs I see next field:
Snap of the swagger docs
There are fields that are specified as extra_data
So, if I specify query params in parameters of my endpoint like this:
#app.get('/')
def my_handler(a: str = Query(None, alias="params[A]")):
return None
Everything works fine. How can I fix it? I want to initialize my pydantic.BaseModel with speacial aliases using this way and avoid usage of query-params in
class MyModel(BaseModel):
a = Field(alias="params[A]")
b = Field(alias="params[B]")
def my_handler(model: MyModel = Depends()):
return model.dict()
I want to generate a description of all available responses (along with code 200 example), which are represented in the code, like here.
from typing import Any
import uvicorn
from fastapi import FastAPI, HTTPException
router = FastAPI()
from pydantic import BaseModel
class FileItemBase(BaseModel):
current_project: str = "Test project"
class FileItemInDBBase(FileItemBase):
id: int
folder_path: str
class Config:
orm_mode = True
class FileResponse(FileItemInDBBase):
pass
#router.get("/", response_model=FileResponse)
def example_code() -> Any:
"""
# beautiful description
to demonstrate functionality
"""
demo=True
if demo:
raise HTTPException(418, "That is a teapot.")
if __name__ =="__main__":
uvicorn.run(router)
What I got with this is such a description.
When I try this out - I got an error response (as expected).
What I want - is the description of an error included in the example responses, like here. A Frontend-developer can look at this description and process such cases in the right way without testing the API.
I know how it can be made within OpenAPI specs.
Is there a way to generate this description with FastAPI?
You can add a responses parameter to your path operation.
Then you can pass your model there. It will create a schema for that model.
class FileItemBase(BaseModel):
current_project: str = "Test project"
#app.get("/", response_model=FileItemBase, responses={418: {"model": FileItemBase}})
def example_code():
"""
# beautiful description
to demonstrate functionality
"""
demo = True
if demo:
raise HTTPException(418, "That is a teapot.")
I'm using FastAPI and now want to add GraphQL using graphene.
I'm using:
fastapi-user
starlette.graphql.GraphQLApp
Here's the routing for GraphQL and how I used fastapi-user package.
import fastapi_users
from starlette.graphql import GraphQLApp
from mypackage.schema import my_schema
...
app.include_router(fastapi_users.router, prefix="/users", tags=["users"])
app.include_router(google_oauth_router, prefix="/google-oauth", tags=["users"])
app.add_route("/", GraphQLApp(schema=my_schema))
...
In the schema, I need to get user information and control the role based auth.
How to, or how can I use get_current_active_user() method with GraphQL schema?
I'm assuming that my_schema inherits graphene.ObjectType and that the first argument is info as shown in the example at:
https://fastapi.tiangolo.com/advanced/graphql/
from graphene import ObjectType, String
class Query(ObjectType):
hello = String(name=String(default_value="stranger"))
def resolve_hello(self, info, name):
return "Hello " + name
info contains the session object with your user information.
info.context['request'].session['user']
I'm trying to update an endpoint which I have defined previously as follows:
class NestedMessage(messages.Message):
foo = messages.StringField(1)
MyResource = endpoints.ResourceContainer(
message_types.VoidMessage,
param1=messages.StringField(1, required=True),
param2=messages.StringField(2),
param3=messages.MessageField(NestedMessage, 3)
)
class MyApi(remote.Service):
#endpoints.method(MyResource, messages.VoidMessage,
path='new', http_method='POST', name='new')
def new(self, request):
# ...
return messages.VoidMessage
I need to change from using a ResourceContainer to a Message class instead (it was unclear which one to use for POST methods).
So I try and update to this:
# NestedMessage unchanged
class NestedMessage(messages.Message):
foo = messages.StringField(1)
class MyMessage(messages.Message):
param1 = messages.StringField(1, required=True)
param2 = messages.StringField(2)
param3 = messages.MessageField(NestedMessage, 3)
class MyApi(remote.Service):
#endpoints.method(MyMessage, messages.VoidMessage,
path='new', http_method='POST', name='new')
def new(self, request):
# ...
return messages.VoidMessage
But when I try the endpoint in /_ah/api/explorer, it just shows a blank page. Checking the result of discovery.apis.getRest on my api, the new MyMessage class isn't listed, and the MyApi.new method is borked due to not having a resource container.
Renaming the method to something else (e.g. MyApi.create) works, but then it breaks the MyApi.new method.
Is there a way to "force" Cloud Endpoints to update all methods?
EDIT This is both on development (localhost) and when deploying.
EDIT 2 It seems the issue only happens with API Explorer. Using the updated endpoint (via the Javascript library for example) uses the working, updated method.
EDIT 3 No wait, I've updated two endpoint methods. Now, both of them are showing fine in API Explorer, but one of them still seems to be "broken" when used with the Javascript client library (i.e. POST payload is passed as URL query parameters, not as JSON body). The other is behaving correctly. What is going on?