Skip to content

Commit

Permalink
Add support for browser caching and tile redirects
Browse files Browse the repository at this point in the history
  • Loading branch information
Wiktor Latanowicz committed Oct 3, 2024
1 parent e507d52 commit 5260374
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 5 deletions.
23 changes: 23 additions & 0 deletions generic_map_api/caching.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,26 @@ def get_tile_bytes(self, z: int, x: int, y: int, params: dict):
if timeout is not NO_CACHE:
self._write_cache(key, value, timeout)
return value

def get_browser_caching_salt(self):
extra = self.view.get_caching_key_extra("ITEMS", self.request)
if not extra:
return None

context_str = json.dumps(extra, default=str)
hasher = hashlib.sha256(context_str.encode("utf-8"))
salt = b64encode(hasher.digest()).decode("utf-8")[:10]

return salt

def add_browser_cache_headers(self, response):
cache_ttl = (
self.view.cache_ttl_browser
or self.view.cache_ttl_items
or self.view.cache_ttl
)
if cache_ttl is NO_CACHE:
return response

response["Cache-Control"] = f"max-age={cache_ttl}"
return response
5 changes: 5 additions & 0 deletions generic_map_api/values.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,8 @@ def full(cls, count=0):
),
count=count,
)


@dataclass
class TileRedirect:
url: str
27 changes: 22 additions & 5 deletions generic_map_api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from django.core.exceptions import BadRequest
from django.db.models import QuerySet
from django.http import Http404, HttpResponse
from django.http import Http404, HttpResponse, HttpResponseRedirect
from rest_framework.decorators import action
from rest_framework.request import Request
from rest_framework.response import Response
Expand All @@ -19,7 +19,14 @@
from .constants import ViewportHandling
from .serializers import BaseFeatureSerializer, BoundingBoxSerializer
from .utils import to_bool
from .values import BaseViewPort, BoundingBox, EmptyViewport, Tile, ViewPort
from .values import (
BaseViewPort,
BoundingBox,
EmptyViewport,
Tile,
TileRedirect,
ViewPort,
)


class MapApiBaseMeta(ABCMeta):
Expand Down Expand Up @@ -52,6 +59,7 @@ class MapApiBaseView(ABC, ViewSet, metaclass=MapApiBaseMeta):
cache_ttl_items = None
cache_ttl_bounds = None
cache_ttl_tile = None
cache_ttl_browser = None

def get_caching_key_extra(
self, fn_name, request, **context
Expand Down Expand Up @@ -202,6 +210,7 @@ def get_meta(self):
"preferred_viewport_chunks": self.preferred_viewport_chunks,
"query_params": self.render_query_params_meta(),
"requirements": self.render_requirements(),
"browser_cache_salt": Cache(self, self.request).get_browser_caching_salt(),
}

def list(self, request):
Expand Down Expand Up @@ -234,7 +243,8 @@ def list(self, request):
response = {
"items": list(serialized_items),
}
return Response(response)
http_response = Response(response)
return cache.add_browser_cache_headers(http_response)

def get_serialized_items(self, viewport: BaseViewPort, params: dict):
items = self.get_items(viewport, params)
Expand Down Expand Up @@ -325,6 +335,7 @@ def get_meta(self):
"category": self.category,
"icon": self.get_icon(),
"query_params": self.render_query_params_meta(),
"browser_cache_salt": Cache(self, self.request).get_browser_caching_salt(),
}

def make_pattern_url(self, action_name, kwargs):
Expand All @@ -342,12 +353,18 @@ def get_url_params(self):
trailing_slash=False,
)
def tile(self, request, z, x, y):
cache = Cache(self, request)
params = self._parse_params(request)
cache = Cache(self, request)
tile_bytes = cache.get_tile_bytes(z, x, y, params)
if not tile_bytes:
return self.render_empty_response(request, z, x, y)
return HttpResponse(tile_bytes, content_type="image/png")
response = self.render_empty_response(request, z, x, y)
if isinstance(tile_bytes, TileRedirect):
response = HttpResponseRedirect(tile_bytes.url)
else:
response = HttpResponse(tile_bytes, content_type="image/png")

return cache.add_browser_cache_headers(response)

def get_tile_bytes(self, z: int, x: int, y: int, params: dict):
return self.get_tile(z, x, y, params)
Expand Down

0 comments on commit 5260374

Please sign in to comment.