From 393637b6adafc89da61d19bf582b2c20747c2200 Mon Sep 17 00:00:00 2001 From: Gwendolyn Date: Fri, 27 Dec 2024 02:25:07 +0100 Subject: [PATCH] fix etag_add feature and overlay stuff and generated api naming thing --- src/c3nav/mapdata/api/base.py | 24 +++++++++++++----------- src/c3nav/mapdata/api/mapdata.py | 24 +++++++++++++++--------- src/c3nav/mapdata/schemas/models.py | 4 +--- 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/c3nav/mapdata/api/base.py b/src/c3nav/mapdata/api/base.py index d70981e5..5368a051 100644 --- a/src/c3nav/mapdata/api/base.py +++ b/src/c3nav/mapdata/api/base.py @@ -49,6 +49,18 @@ def api_etag(permissions=True, quests=False, etag_func=AccessPermission.etag_fun raw_etag += 'all' if request.user.is_superuser else f':{','.join(request.user_permissions.quests)}' if base_mapdata: raw_etag += ':%d' % request.user_permissions.can_access_base_mapdata + + if etag_add_key: + etag_add_cache_key = ( + f'mapdata:etag_add:{etag_add_key[1]}:{getattr(kwargs[etag_add_key[0]], etag_add_key[1])}' + ) + etag_add = cache.get(etag_add_cache_key, None) + if etag_add is None: + etag_add = int(time.time()) + cache.set(etag_add_cache_key, etag_add, 300) + raw_etag += ':%d' % etag_add + + etag = quote_etag(raw_etag) response = get_conditional_response(request, etag) @@ -68,19 +80,9 @@ def api_etag(permissions=True, quests=False, etag_func=AccessPermission.etag_fun value = model_dump() data[name] = value - etag_add = '' - if etag_add_key: - etag_add_cache_key = ( - f'mapdata:etag_add:{etag_add_key[1]}:{getattr(kwargs[etag_add_key[0]], etag_add_key[1])}' - ) - etag_add = cache.get(etag_add_cache_key, None) - if etag_add is None: - etag_add = int(time.time()) - cache.set(etag_add_cache_key, etag_add, 300) - cache_key = 'mapdata:api:%s:%s:%s:%s' % ( + cache_key = 'mapdata:api:%s:%s:%s' % ( request.resolver_match.route.replace('/', '-').strip('-'), raw_etag, - etag_add, json.dumps(data, separators=(',', ':'), sort_keys=True, cls=DjangoJSONEncoder), ) diff --git a/src/c3nav/mapdata/api/mapdata.py b/src/c3nav/mapdata/api/mapdata.py index 9781c527..32cf0b4d 100644 --- a/src/c3nav/mapdata/api/mapdata.py +++ b/src/c3nav/mapdata/api/mapdata.py @@ -80,6 +80,12 @@ class MapdataEndpoint: schema: Type[BaseSchema] filters: Type[FilterSchema] | None = None no_cache: bool = False + + # etag_add_key is a weird, limited and hacky solution to add cache/etag invalidation for data that can be changed without triggering mapupdate + # if set, its value *must* be the name of an attribute in the filter schema, and the value of that filter will be used in the etag/cache key + # all api endpoints that have the same etag_add_key value have shared cache/etag invalidation, i.e. if one is invalidated then all are + # the cache is invalidated by deleting the cache key "mapdata:etag_add::" where filter-value is the value of the + # filter attribute that etag_add_key references etag_add_key: Optional[str] = None name: Optional[str] = None @@ -91,6 +97,10 @@ class MapdataEndpoint: def endpoint_name(self): return self.name if self.name is not None else self.model._meta.default_related_name + @property + def endpoint_operation_name(self): + return self.name if self.name is not None else self.model_name + @dataclass class MapdataAPIBuilder: @@ -142,7 +152,8 @@ class MapdataAPIBuilder: call_func=mapdata_list_endpoint, add_call_params={"model": endpoint.model.__name__} ) - list_func.__name__ = f"{endpoint.model_name}_list" + list_func.__name__ = f"{endpoint.endpoint_operation_name}_list" + if not endpoint.no_cache: list_func = api_etag( @@ -168,7 +179,7 @@ class MapdataAPIBuilder: call_func=mapdata_retrieve_endpoint, add_call_params={"model": endpoint.model.__name__, "pk": id_field} ) - list_func.__name__ = f"{endpoint.model_name}_by_id" + list_func.__name__ = f"{endpoint.endpoint_operation_name}_by_id" self.router.get(f'/{endpoint.endpoint_name}/{{{id_field}}}/', summary=f"{endpoint.model_name} by ID", tags=[f"mapdata-{tag}"], description=schema_description(endpoint.schema), @@ -243,7 +254,6 @@ mapdata_endpoints: dict[str, list[MapdataEndpoint]] = { model=DataOverlayFeature, schema=DataOverlayFeatureGeometrySchema, filters=ByOverlayFilter, - etag_add_key="overlay", name='dataoverlayfeaturegeometries' ), MapdataEndpoint( @@ -325,13 +335,9 @@ mapdata_endpoints: dict[str, list[MapdataEndpoint]] = { MapdataAPIBuilder(router=mapdata_api_router).build_all_endpoints(mapdata_endpoints) -@mapdata_api_router.post('/dataoverlayfeatures/{id}', summary="update a data overlay feature (including geometries)", +@mapdata_api_router.post('/dataoverlayfeatures/{id}', summary="update a data overlay feature (no geometries)", response={204: None, **API404.dict(), **auth_permission_responses}) def update_data_overlay_feature(request, id: int, parameters: DataOverlayFeatureUpdateSchema): - """ - update the data overlay feature - """ - feature = get_object_or_404(DataOverlayFeature, id=id) if feature.overlay.edit_access_restriction_id is None or feature.overlay.edit_access_restriction_id not in AccessPermission.get_for_request( @@ -350,7 +356,7 @@ def update_data_overlay_feature(request, id: int, parameters: DataOverlayFeature return 204, None -@mapdata_api_router.post('/dataoverlayfeatures-bulk', summary="bulk-update data overlays (including geometries)", +@mapdata_api_router.post('/dataoverlayfeatures-bulk', summary="bulk-update data overlays (no geometries)", response={204: None, **API404.dict(), **auth_permission_responses}) def update_data_overlay_features_bulk(request, parameters: DataOverlayFeatureBulkUpdateSchema): permissions = AccessPermission.get_for_request(request) diff --git a/src/c3nav/mapdata/schemas/models.py b/src/c3nav/mapdata/schemas/models.py index fe415a93..ee9e87d1 100644 --- a/src/c3nav/mapdata/schemas/models.py +++ b/src/c3nav/mapdata/schemas/models.py @@ -408,7 +408,6 @@ class DataOverlayFeatureUpdateSchema(BaseSchema): An update to a data overlay feature. """ level_id: Optional[PositiveInt] = None - geometry: Optional[AnyGeometrySchema] = None stroke_color: Optional[str] = None stroke_width: Optional[float] = None stroke_opacity: Optional[float] = None @@ -423,11 +422,10 @@ class DataOverlayFeatureUpdateSchema(BaseSchema): class DataOverlayFeatureBulkUpdateItemSchema(BaseSchema): """ - An item of a bulk update to data overlay features. + An item of a bulk update to data overlay features (no geometries). """ id: PositiveInt level_id: Optional[PositiveInt] = None - geometry: Optional[AnyGeometrySchema] = None stroke_color: Optional[str] = None stroke_width: Optional[float] = None stroke_opacity: Optional[float] = None