add API caching, OMG?!

This commit is contained in:
Laura Klünder 2023-11-24 14:35:48 +01:00
parent 687c5b4276
commit 292c152473
2 changed files with 55 additions and 21 deletions

View file

@ -7,6 +7,7 @@ from django.shortcuts import redirect
from ninja import Query
from ninja import Router as APIRouter
from ninja import Schema
from ninja.decorators import decorate_view
from pydantic import Field as APIField
from c3nav.api.exceptions import API404
@ -15,6 +16,7 @@ from c3nav.api.utils import NonEmptyStr
from c3nav.mapdata.models import Source
from c3nav.mapdata.models.access import AccessPermission
from c3nav.mapdata.models.locations import DynamicLocation, LocationRedirect, Position
from c3nav.mapdata.newapi.base import newapi_etag
from c3nav.mapdata.schemas.filters import BySearchableFilter, RemoveGeometryFilter
from c3nav.mapdata.schemas.model_base import AnyLocationID, AnyPositionID
from c3nav.mapdata.schemas.models import (AnyPositionStatusSchema, FullListableLocationSchema, FullLocationSchema,
@ -29,6 +31,7 @@ map_api_router = APIRouter(tags=["map"])
@map_api_router.get('/bounds/', summary="Get map boundaries",
response={200: BoundsSchema, **auth_responses})
@newapi_etag(permissions=False)
def bounds(request):
return {
"bounds": Source.max_bounds(),
@ -76,6 +79,7 @@ def _location_list(request, detailed: bool, filters: LocationListFilters):
@map_api_router.get('/locations/',
response={200: list[SlimListableLocationSchema], **validate_responses, **auth_responses},
summary="Get locations (with most important attributes)")
@newapi_etag(base_mapdata=True)
def location_list(request, filters: Query[LocationListFilters]):
return _location_list(request, detailed=False, filters=filters)
@ -83,6 +87,7 @@ def location_list(request, filters: Query[LocationListFilters]):
@map_api_router.get('/locations/full/',
response={200: list[FullListableLocationSchema], **validate_responses, **auth_responses},
summary="Get locations (with all attributes)")
@newapi_etag(base_mapdata=True)
def location_list_full(request, filters: Query[LocationListFilters]):
return _location_list(request, detailed=True, filters=filters)
@ -152,6 +157,7 @@ class ShowRedirects(Schema):
response={200: SlimLocationSchema, **API404.dict(), **validate_responses, **auth_responses},
summary="Get location by ID (with most important attributes)",
description="a numeric ID for a map location or a string ID for generated locations can be used")
@newapi_etag(base_mapdata=True)
def location_by_id(request, location_id: AnyLocationID, filters: Query[RemoveGeometryFilter],
redirects: Query[ShowRedirects]):
return _location_retrieve(
@ -165,6 +171,7 @@ def location_by_id(request, location_id: AnyLocationID, filters: Query[RemoveGeo
response={200: FullLocationSchema, **API404.dict(), **validate_responses, **auth_responses},
summary="Get location by ID (with all attributes)",
description="a numeric ID for a map location or a string ID for generated locations can be used")
@newapi_etag(base_mapdata=True)
def location_by_id_full(request, location_id: AnyLocationID, filters: Query[RemoveGeometryFilter],
redirects: Query[ShowRedirects]):
return _location_retrieve(
@ -178,6 +185,7 @@ def location_by_id_full(request, location_id: AnyLocationID, filters: Query[Remo
response={200: LocationDisplay, **API404.dict(), **auth_responses},
summary="Get location display data by ID",
description="a numeric ID for a map location or a string ID for generated locations can be used")
@newapi_etag(base_mapdata=True)
def location_by_id_display(request, location_id: AnyLocationID):
return _location_display(
request,
@ -189,6 +197,7 @@ def location_by_id_display(request, location_id: AnyLocationID):
response={200: LocationGeometry, **API404.dict(), **auth_responses},
summary="Get location geometry (if available) by ID",
description="a numeric ID for a map location or a string ID for generated locations can be used")
@newapi_etag(base_mapdata=True)
def location_by_id_geometry(request, location_id: AnyLocationID):
return _location_geometry(
request,
@ -199,6 +208,7 @@ def location_by_id_geometry(request, location_id: AnyLocationID):
@map_api_router.get('/locations/by-slug/{location_slug}/',
response={200: SlimLocationSchema, **API404.dict(), **validate_responses, **auth_responses},
summary="Get location by slug (with most important attributes)")
@newapi_etag(base_mapdata=True)
def location_by_slug(request, location_slug: NonEmptyStr, filters: Query[RemoveGeometryFilter],
redirects: Query[ShowRedirects]):
return _location_retrieve(
@ -211,6 +221,7 @@ def location_by_slug(request, location_slug: NonEmptyStr, filters: Query[RemoveG
@map_api_router.get('/locations/by-slug/{location_slug}/full/',
response={200: FullLocationSchema, **API404.dict(), **validate_responses, **auth_responses},
summary="Get location by slug (with all attributes)")
@newapi_etag(base_mapdata=True)
def location_by_slug_full(request, location_slug: NonEmptyStr, filters: Query[RemoveGeometryFilter],
redirects: Query[ShowRedirects]):
return _location_retrieve(
@ -223,6 +234,7 @@ def location_by_slug_full(request, location_slug: NonEmptyStr, filters: Query[Re
@map_api_router.get('/locations/by-slug/{location_slug}/display/',
response={200: LocationDisplay, **API404.dict(), **auth_responses},
summary="Get location display data by slug")
@newapi_etag(base_mapdata=True)
def location_by_slug_display(request, location_slug: NonEmptyStr):
return _location_display(
request,
@ -233,6 +245,7 @@ def location_by_slug_display(request, location_slug: NonEmptyStr):
@map_api_router.get('/locations/by-slug/{location_slug}/geometry/',
response={200: LocationGeometry, **API404.dict(), **auth_responses},
summary="Get location geometry (if available) by slug")
@newapi_etag(base_mapdata=True)
def location_by_slug_geometry(request, location_slug: NonEmptyStr):
return _location_geometry(
request,
@ -245,6 +258,7 @@ def location_by_slug_geometry(request, location_slug: NonEmptyStr):
summary="get current position of a moving object",
description="a numeric ID for a dynamic location or a string ID for the position secret can be used")
def get_current_position_by_id(request, position_id: AnyPositionID):
# no caching for obvious reasons!
location = None
if isinstance(position_id, int) or position_id.isdigit():
location = get_location_by_id_for_request(position_id, request)

View file

@ -3,7 +3,6 @@ from typing import Optional, Sequence, Type
from django.db.models import Model
from ninja import Query
from ninja import Router as APIRouter
from ninja.pagination import paginate
from c3nav.api.exceptions import API404
from c3nav.api.newauth import auth_responses, validate_responses
@ -14,6 +13,7 @@ from c3nav.mapdata.models.access import AccessPermission, AccessRestriction, Acc
from c3nav.mapdata.models.geometry.space import (POI, Column, CrossDescription, LeaveDescription, LineObstacle,
Obstacle, Ramp)
from c3nav.mapdata.models.locations import DynamicLocation
from c3nav.mapdata.newapi.base import newapi_etag
from c3nav.mapdata.schemas.filters import (ByCategoryFilter, ByGroupFilter, ByOnTopOfFilter, FilterSchema,
LevelGeometryFilter, SpaceGeometryFilter)
from c3nav.mapdata.schemas.models import (AccessRestrictionGroupSchema, AccessRestrictionSchema, AreaSchema,
@ -85,7 +85,7 @@ class LevelFilters(ByGroupFilter, ByOnTopOfFilter):
@mapdata_api_router.get('/levels/',
response={200: list[LevelSchema], **validate_responses, **auth_responses},
summary="Get level list")
@paginate
@newapi_etag()
def level_list(request, filters: Query[LevelFilters]):
# todo cache?
return mapdata_list_endpoint(request, model=Level, filters=filters)
@ -94,6 +94,7 @@ def level_list(request, filters: Query[LevelFilters]):
@mapdata_api_router.get('/levels/{level_id}/',
response={200: LevelSchema, **API404.dict(), **auth_responses},
summary="Get level by ID")
@newapi_etag()
def level_by_id(request, level_id: int):
# todo: access, caching, filtering, etc
return mapdata_retrieve_endpoint(request, Level, pk=level_id)
@ -107,7 +108,7 @@ Buildings
@mapdata_api_router.get('/buildings/',
response={200: list[BuildingSchema], **validate_responses, **auth_responses},
summary="Get building list")
@paginate
@newapi_etag(base_mapdata=True)
def building_list(request, filters: Query[LevelGeometryFilter]):
# todo cache?
return mapdata_list_endpoint(request, model=Building, filters=filters)
@ -116,6 +117,7 @@ def building_list(request, filters: Query[LevelGeometryFilter]):
@mapdata_api_router.get('/buildings/{building_id}/',
response={200: BuildingSchema, **API404.dict(), **auth_responses},
summary="Get building by ID")
@newapi_etag(base_mapdata=True)
def building_by_id(request, building_id: int):
# todo: access, caching, filtering, etc
return mapdata_retrieve_endpoint(request, Building, pk=building_id)
@ -133,7 +135,7 @@ class SpaceFilters(ByGroupFilter, LevelGeometryFilter):
@mapdata_api_router.get('/spaces/',
response={200: list[SpaceSchema], **validate_responses, **auth_responses},
summary="Get space list")
@paginate
@newapi_etag(base_mapdata=True)
def space_list(request, filters: Query[SpaceFilters]):
# todo cache?
return mapdata_list_endpoint(request, model=Space, filters=filters)
@ -142,6 +144,7 @@ def space_list(request, filters: Query[SpaceFilters]):
@mapdata_api_router.get('/space/{space_id}/',
response={200: SpaceSchema, **API404.dict(), **auth_responses},
summary="Get space by ID")
@newapi_etag(base_mapdata=True)
def space_by_id(request, space_id: int):
# todo: access, caching, filtering, etc
return mapdata_retrieve_endpoint(request, Space, pk=space_id)
@ -155,7 +158,7 @@ Doors
@mapdata_api_router.get('/doors/',
response={200: list[DoorSchema], **validate_responses, **auth_responses},
summary="Get door list")
@paginate
@newapi_etag(base_mapdata=True)
def door_list(request, filters: Query[LevelGeometryFilter]):
# todo cache?
return mapdata_list_endpoint(request, model=Door, filters=filters)
@ -164,6 +167,7 @@ def door_list(request, filters: Query[LevelGeometryFilter]):
@mapdata_api_router.get('/doors/{door_id}/',
response={200: DoorSchema, **API404.dict(), **auth_responses},
summary="Get door by ID")
@newapi_etag(base_mapdata=True)
def door_by_id(request, door_id: int):
# todo: access, caching, filtering, etc
return mapdata_retrieve_endpoint(request, Door, pk=door_id)
@ -177,7 +181,7 @@ Holes
@mapdata_api_router.get('/holes/',
response={200: list[HoleSchema], **validate_responses, **auth_responses},
summary="Get hole list")
@paginate
@newapi_etag()
def hole_list(request, filters: Query[SpaceGeometryFilter]):
# todo cache?
return mapdata_list_endpoint(request, model=Hole, filters=filters)
@ -186,6 +190,7 @@ def hole_list(request, filters: Query[SpaceGeometryFilter]):
@mapdata_api_router.get('/holes/{hole_id}/',
response={200: HoleSchema, **API404.dict(), **auth_responses},
summary="Get hole by ID")
@newapi_etag()
def hole_by_id(request, hole_id: int):
# todo: access, caching, filtering, etc
return mapdata_retrieve_endpoint(request, Hole, pk=hole_id)
@ -203,7 +208,7 @@ class AreaFilters(ByGroupFilter, SpaceGeometryFilter):
@mapdata_api_router.get('/areas/',
response={200: list[AreaSchema], **validate_responses, **auth_responses},
summary="Get area list")
@paginate
@newapi_etag()
def area_list(request, filters: Query[AreaFilters]):
# todo cache?
return mapdata_list_endpoint(request, model=Area, filters=filters)
@ -212,6 +217,7 @@ def area_list(request, filters: Query[AreaFilters]):
@mapdata_api_router.get('/areas/{area_id}/',
response={200: AreaSchema, **API404.dict(), **auth_responses},
summary="Get area by ID")
@newapi_etag()
def area_by_id(request, area_id: int):
# todo: access, caching, filtering, etc
return mapdata_retrieve_endpoint(request, Area, pk=area_id)
@ -225,7 +231,7 @@ Stairs
@mapdata_api_router.get('/stairs/',
response={200: list[StairSchema], **validate_responses, **auth_responses},
summary="Get stair list")
@paginate
@newapi_etag()
def stair_list(request, filters: Query[SpaceGeometryFilter]):
# todo cache?
return mapdata_list_endpoint(request, model=Stair, filters=filters)
@ -234,6 +240,7 @@ def stair_list(request, filters: Query[SpaceGeometryFilter]):
@mapdata_api_router.get('/stairs/{stair_id}/',
response={200: StairSchema, **API404.dict(), **auth_responses},
summary="Get stair by ID")
@newapi_etag()
def stair_by_id(request, stair_id: int):
# todo: access, caching, filtering, etc
return mapdata_retrieve_endpoint(request, Stair, pk=stair_id)
@ -247,7 +254,7 @@ Ramps
@mapdata_api_router.get('/ramps/',
response={200: list[RampSchema], **validate_responses, **auth_responses},
summary="Get ramp list")
@paginate
@newapi_etag()
def ramp_list(request, filters: Query[SpaceGeometryFilter]):
# todo cache?
return mapdata_list_endpoint(request, model=Ramp, filters=filters)
@ -256,6 +263,7 @@ def ramp_list(request, filters: Query[SpaceGeometryFilter]):
@mapdata_api_router.get('/ramps/{ramp_id}/',
response={200: RampSchema, **API404.dict(), **auth_responses},
summary="Get ramp by ID")
@newapi_etag()
def ramp_by_id(request, ramp_id: int):
# todo: access, caching, filtering, etc
return mapdata_retrieve_endpoint(request, Ramp, pk=ramp_id)
@ -269,7 +277,7 @@ Obstacles
@mapdata_api_router.get('/obstacles/',
response={200: list[ObstacleSchema], **validate_responses, **auth_responses},
summary="Get obstacle list")
@paginate
@newapi_etag()
def obstacle_list(request, filters: Query[SpaceGeometryFilter]):
# todo cache?
return mapdata_list_endpoint(request, model=Obstacle, filters=filters)
@ -278,6 +286,7 @@ def obstacle_list(request, filters: Query[SpaceGeometryFilter]):
@mapdata_api_router.get('/obstacles/{obstacle_id}/',
response={200: ObstacleSchema, **API404.dict(), **auth_responses},
summary="Get obstacle by ID")
@newapi_etag()
def obstacle_by_id(request, obstacle_id: int):
# todo: access, caching, filtering, etc
return mapdata_retrieve_endpoint(request, Obstacle, pk=obstacle_id)
@ -291,7 +300,7 @@ LineObstacles
@mapdata_api_router.get('/lineobstacles/',
response={200: list[LineObstacleSchema], **validate_responses, **auth_responses},
summary="Get line obstacle list")
@paginate
@newapi_etag()
def lineobstacle_list(request, filters: Query[SpaceGeometryFilter]):
# todo cache?
return mapdata_list_endpoint(request, model=LineObstacle, filters=filters)
@ -300,6 +309,7 @@ def lineobstacle_list(request, filters: Query[SpaceGeometryFilter]):
@mapdata_api_router.get('/lineobstacles/{lineobstacle_id}/',
response={200: LineObstacleSchema, **API404.dict(), **auth_responses},
summary="Get line obstacle by ID")
@newapi_etag()
def lineobstacle_by_id(request, lineobstacle_id: int):
# todo: access, caching, filtering, etc
return mapdata_retrieve_endpoint(request, LineObstacle, pk=lineobstacle_id)
@ -313,7 +323,7 @@ Columns
@mapdata_api_router.get('/columns/',
response={200: list[ColumnSchema], **validate_responses, **auth_responses},
summary="Get column list")
@paginate
@newapi_etag()
def column_list(request, filters: Query[SpaceGeometryFilter]):
# todo cache?
return mapdata_list_endpoint(request, model=Column, filters=filters)
@ -322,6 +332,7 @@ def column_list(request, filters: Query[SpaceGeometryFilter]):
@mapdata_api_router.get('/columns/{column_id}/',
response={200: ColumnSchema, **API404.dict(), **auth_responses},
summary="Get column by ID")
@newapi_etag()
def column_by_id(request, column_id: int):
# todo: access, caching, filtering, etc
return mapdata_retrieve_endpoint(request, Column, pk=column_id)
@ -335,7 +346,7 @@ POIs
@mapdata_api_router.get('/pois/',
response={200: list[POISchema], **validate_responses, **auth_responses},
summary="Get POI list")
@paginate
@newapi_etag()
def poi_list(request, filters: Query[SpaceGeometryFilter]):
# todo cache?
return mapdata_list_endpoint(request, model=POI, filters=filters)
@ -344,6 +355,7 @@ def poi_list(request, filters: Query[SpaceGeometryFilter]):
@mapdata_api_router.get('/pois/{poi_id}/',
response={200: POISchema, **API404.dict(), **auth_responses},
summary="Get POI by ID")
@newapi_etag()
def poi_by_id(request, poi_id: int):
# todo: access, caching, filtering, etc
return mapdata_retrieve_endpoint(request, POI, pk=poi_id)
@ -357,7 +369,7 @@ LeaveDescriptions
@mapdata_api_router.get('/leavedescriptions/',
response={200: list[LeaveDescriptionSchema], **validate_responses, **auth_responses},
summary="Get leave description list")
@paginate
@newapi_etag()
def leavedescription_list(request, filters: Query[SpaceGeometryFilter]):
# todo cache?
return mapdata_list_endpoint(request, model=LeaveDescription, filters=filters)
@ -366,6 +378,7 @@ def leavedescription_list(request, filters: Query[SpaceGeometryFilter]):
@mapdata_api_router.get('/leavedescriptions/{leavedescription_id}/',
response={200: LeaveDescriptionSchema, **API404.dict(), **auth_responses},
summary="Get leave description by ID")
@newapi_etag()
def leavedescription_by_id(request, leavedescription_id: int):
# todo: access, caching, filtering, etc
return mapdata_retrieve_endpoint(request, LeaveDescription, pk=leavedescription_id)
@ -379,7 +392,7 @@ CrossDescriptions
@mapdata_api_router.get('/crossdescriptions/',
response={200: list[CrossDescriptionSchema], **validate_responses, **auth_responses},
summary="Get cross description list")
@paginate
@newapi_etag()
def crossdescription_list(request, filters: Query[SpaceGeometryFilter]):
# todo cache?
return mapdata_list_endpoint(request, model=CrossDescription, filters=filters)
@ -388,6 +401,7 @@ def crossdescription_list(request, filters: Query[SpaceGeometryFilter]):
@mapdata_api_router.get('/crossdescriptions/{crossdescription_id}/',
response={200: CrossDescriptionSchema, **API404.dict(), **auth_responses},
summary="Get cross description by ID")
@newapi_etag()
def crossdescription_by_id(request, crossdescription_id: int):
# todo: access, caching, filtering, etc
return mapdata_retrieve_endpoint(request, CrossDescription, pk=crossdescription_id)
@ -401,7 +415,7 @@ LocationGroup
@mapdata_api_router.get('/locationgroups/',
response={200: list[LocationGroupSchema], **validate_responses, **auth_responses},
summary="Get location group list")
@paginate
@newapi_etag()
def locationgroup_list(request, filters: Query[ByCategoryFilter]):
# todo cache?
return mapdata_list_endpoint(request, model=LocationGroup, filters=filters)
@ -410,6 +424,7 @@ def locationgroup_list(request, filters: Query[ByCategoryFilter]):
@mapdata_api_router.get('/locationgroups/{locationgroup_id}/',
response={200: LocationGroupSchema, **API404.dict(), **auth_responses},
summary="Get location group by ID")
@newapi_etag()
def locationgroup_by_id(request, locationgroup_id: int):
# todo: access, caching, filtering, etc
return mapdata_retrieve_endpoint(request, LocationGroup, pk=locationgroup_id)
@ -423,7 +438,7 @@ LocationGroupCategories
@mapdata_api_router.get('/locationgroupcategories/',
response={200: list[LocationGroupCategorySchema], **auth_responses},
summary="Get location group category list")
@paginate
@newapi_etag()
def locationgroupcategory_list(request):
# todo cache?
return mapdata_list_endpoint(request, model=LocationGroupCategory)
@ -432,6 +447,7 @@ def locationgroupcategory_list(request):
@mapdata_api_router.get('/locationgroupcategories/{category_id}/',
response={200: LocationGroupCategorySchema, **API404.dict(), **auth_responses},
summary="Get location group category by ID")
@newapi_etag()
def locationgroupcategory_by_id(request, category_id: int):
# todo: access, caching, filtering, etc
return mapdata_retrieve_endpoint(request, LocationGroupCategory, pk=category_id)
@ -445,7 +461,7 @@ Sources
@mapdata_api_router.get('/sources/',
response={200: list[SourceSchema], **auth_responses},
summary="Get source list")
@paginate
@newapi_etag()
def source_list(request):
# todo cache?
return mapdata_list_endpoint(request, model=Source)
@ -454,6 +470,7 @@ def source_list(request):
@mapdata_api_router.get('/sources/{source_id}/',
response={200: SourceSchema, **API404.dict(), **auth_responses},
summary="Get source by ID")
@newapi_etag()
def source_by_id(request, source_id: int):
# todo: access, caching, filtering, etc
return mapdata_retrieve_endpoint(request, Source, pk=source_id)
@ -467,7 +484,7 @@ AccessRestrictions
@mapdata_api_router.get('/accessrestrictions/',
response={200: list[AccessRestrictionSchema], **auth_responses},
summary="Get access restriction list")
@paginate
@newapi_etag()
def accessrestriction_list(request):
# todo cache?
return mapdata_list_endpoint(request, model=AccessRestriction)
@ -476,6 +493,7 @@ def accessrestriction_list(request):
@mapdata_api_router.get('/accessrestrictions/{accessrestriction_id}/',
response={200: AccessRestrictionSchema, **API404.dict(), **auth_responses},
summary="Get access restriction by ID")
@newapi_etag()
def accessrestriction_by_id(request, accessrestriction_id: int):
# todo: access, caching, filtering, etc
return mapdata_retrieve_endpoint(request, AccessRestriction, pk=accessrestriction_id)
@ -489,7 +507,7 @@ AccessRestrictionGroups
@mapdata_api_router.get('/accessrestrictiongroups/',
response={200: list[AccessRestrictionGroupSchema], **auth_responses},
summary="Get access restriction group list")
@paginate
@newapi_etag()
def accessrestrictiongroup_list(request):
# todo cache?
return mapdata_list_endpoint(request, model=AccessRestrictionGroup)
@ -498,6 +516,7 @@ def accessrestrictiongroup_list(request):
@mapdata_api_router.get('/accessrestrictiongroups/{group_id}/',
response={200: AccessRestrictionGroupSchema, **API404.dict(), **auth_responses},
summary="Get access restriction group by ID")
@newapi_etag()
def accessrestrictiongroups_by_id(request, group_id: int):
# todo: access, caching, filtering, etc
return mapdata_retrieve_endpoint(request, AccessRestrictionGroup, pk=group_id)
@ -511,7 +530,7 @@ DynamicLocations
@mapdata_api_router.get('/dynamiclocations/',
response={200: list[DynamicLocationSchema], **auth_responses},
summary="Get dynamic location list")
@paginate
@newapi_etag()
def dynamiclocation_list(request):
# todo cache?
return mapdata_list_endpoint(request, model=DynamicLocation)
@ -520,6 +539,7 @@ def dynamiclocation_list(request):
@mapdata_api_router.get('/dynamiclocations/{dynamiclocation_id}/',
response={200: DynamicLocationSchema, **API404.dict(), **auth_responses},
summary="Get dynamic location by ID")
@newapi_etag()
def dynamiclocation_by_id(request, dynamiclocation_id: int):
# todo: access, caching, filtering, etc
return mapdata_retrieve_endpoint(request, DynamicLocation, pk=dynamiclocation_id)