team-3/src/c3nav/api/ninja.py

153 lines
4.7 KiB
Python
Raw Normal View History

from ninja import NinjaAPI, Redoc, Swagger
2023-12-03 18:46:02 +01:00
from ninja.openapi.docs import DocsBase
from ninja.operation import Operation
from ninja.schema import NinjaGenerateJsonSchema
from c3nav.api.auth import APIKeyAuth
from c3nav.api.exceptions import CustomAPIException
class c3navAPI(NinjaAPI):
def get_openapi_operation_id(self, operation: Operation) -> str:
name = operation.view_func.__name__
result = f"c3nav_{operation.tags[0]}_{name}"
return result
2023-12-03 18:46:02 +01:00
class SwaggerAndRedoc(DocsBase):
swagger_config = Swagger(settings={
"persistAuthorization": True,
"defaultModelRendering": "model",
})
redoc_config = Redoc(settings={
2023-12-03 22:52:06 +01:00
"hideOneOfDescription": True,
"expandSingleSchemaField": True,
"jsonSampleExpandLevel": 5,
2023-12-03 22:52:06 +01:00
"expandResponses": "200",
"hideSingleRequestSampleTab": True,
"nativeScrollbars": True,
"simpleOneOfTypeLabel": True,
2023-12-03 18:46:02 +01:00
})
def render_page(self, request, api):
print(request.GET)
if request.GET.get('swagger', None) is not None:
return self.swagger_config.render_page(request, api)
return self.redoc_config.render_page(request, api)
description = """
2023-12-03 19:04:23 +01:00
Nearly all endpoints require authentication, but guest authentication can be used.
API endpoints may change to add more features and properties, but we will attempt to keep it backwards-compatible.
2023-12-03 18:46:02 +01:00
We provide two API documentation viewers:
* [Redoc](/api/v2/): more comprehensive and clean *(default)*
2023-12-03 19:04:23 +01:00
* [Swagger](/api/v2/?swagger): less good, but has a built-in API client
We recommend reading the documentation using Redoc, and either using the Code examples provided next to each request,
or switching to swagger if you want an in-browser API client.
2023-12-03 18:46:02 +01:00
""".strip()
ninja_api = c3navAPI(
title="c3nav API",
version="v2",
description=description,
docs_url="/",
2023-12-03 19:04:23 +01:00
docs=SwaggerAndRedoc(),
auth=APIKeyAuth(),
openapi_extra={
"tags": [
{
"name": "auth",
2023-12-03 19:04:23 +01:00
"x-displayName": "Authentication",
"description": "Get and manage API access",
},
2023-12-03 19:25:44 +01:00
{
"name": "updates",
"x-displayName": "Updates",
"description": "Get regular updates",
},
{
"name": "map",
"x-displayName": "Map",
"description": "Common map endpoints",
},
{
"name": "routing",
2023-12-03 19:04:23 +01:00
"x-displayName": "Routing",
"description": "Calculate routes",
},
{
"name": "positioning",
2023-12-03 19:04:23 +01:00
"x-displayName": "Positioning",
"description": "Determine your position",
},
{
"name": "mapdata-root",
"x-displayName": "Root map data",
"description": "Objects that don't belong to a level or space",
},
{
"name": "mapdata-level",
"x-displayName": "Level map data",
"description": "Objects that belong to a level",
},
{
"name": "mapdata-space",
"x-displayName": "Space map data",
"description": "Objects that belong to a space",
},
2023-12-01 22:59:57 +01:00
{
"name": "editor",
2023-12-03 19:04:23 +01:00
"x-displayName": "Editor",
2023-12-01 22:59:57 +01:00
"description": "Endpoints for the editor and to interface with the editor",
},
{
"name": "mesh",
2023-12-03 19:04:23 +01:00
"x-displayName": "Mesh",
"description": "Manage the location node mesh network",
},
],
"x-tagGroups": [
{
"name": "Setup",
"tags": ["auth", "updates"],
},
{
"name": "Main",
"tags": ["map", "routing", "positioning"],
},
{
"name": "Raw map data",
"tags": ["mapdata-root", "mapdata-level", "mapdata-space"],
},
{
"name": "Other",
"tags": ["editor", "mesh"],
},
]
}
)
"""
ugly hack: remove schema from the end of definition names
"""
orig_normalize_name = NinjaGenerateJsonSchema.normalize_name
def wrap_normalize_name(self, name: str): # noqa
return orig_normalize_name(self, name).removesuffix('Schema')
NinjaGenerateJsonSchema.normalize_name = wrap_normalize_name # noqa
@ninja_api.exception_handler(CustomAPIException)
def on_invalid_token(request, exc):
return ninja_api.create_response(request, {"detail": exc.detail}, status=exc.status_code)