Skip to content

Commit e87f80d

Browse files
authored
Merge pull request #1147 from nextcloud/cache-apps.json
`Last Modified` header support + prod setup optimisation
2 parents 2bedfc7 + 3e0039b commit e87f80d

File tree

8 files changed

+202
-28
lines changed

8 files changed

+202
-28
lines changed

.github/workflows/yarn.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
steps:
3434
- uses: actions/checkout@v4
3535
- name: Install dependencies
36-
run: sudo apt install gettext netcat-openbsd xvfb
36+
run: sudo apt install gettext netcat-openbsd xvfb redis-server
3737

3838
- name: Set up Python ${{ matrix.python-version }}
3939
uses: actions/setup-python@v4

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,4 @@ newrelic.ini
3838
/config
3939
.DS_Store
4040
/nextcloudappstore/static
41+
/dev/

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22

33
## [Unreleased]
44

5+
## [4.3.2] - 2023-09-23
6+
7+
### Added
8+
9+
- Support of `Last-Modified`, e.g. `If-Modified-Since:` header for the Rest API endpoints. #1147
10+
- `redis` python dependency for `django-allauth`.
11+
12+
### Changed
13+
14+
- `/api/v1/apps.json` endpoint now ALWAYS return gzipped data. #1147
15+
516
## [4.3.1] - 2023-09-19
617

718
### Changed

nextcloudappstore/api/v1/urls.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from django.urls import re_path
2-
from django.views.decorators.http import etag
2+
from django.views.decorators.http import condition, etag
33

44
from nextcloudappstore.api.v1.views import (
55
AppRatingView,
@@ -15,8 +15,11 @@
1515
from nextcloudappstore.core.caching import (
1616
app_ratings_etag,
1717
apps_all_etag,
18+
apps_all_last_modified,
1819
apps_etag,
20+
apps_last_modified,
1921
categories_etag,
22+
categories_last_modified,
2023
nextcloud_release_etag,
2124
)
2225
from nextcloudappstore.core.versioning import SEMVER_REGEX
@@ -25,8 +28,12 @@
2528

2629
urlpatterns = [
2730
re_path(r"^platforms\.json$", etag(nextcloud_release_etag)(NextcloudReleaseView.as_view()), name="platforms"),
28-
re_path(r"^platform/(?P<version>\d+\.\d+\.\d+)/apps\.json$", etag(apps_etag)(AppView.as_view()), name="app"),
29-
re_path(r"^apps\.json$", etag(apps_all_etag)(AppsView.as_view()), name="apps"),
31+
re_path(
32+
r"^platform/(?P<version>\d+\.\d+\.\d+)/apps\.json$",
33+
condition(apps_etag, apps_last_modified)(AppView.as_view()),
34+
name="app",
35+
),
36+
re_path(r"^apps\.json$", condition(apps_all_etag, apps_all_last_modified)(AppsView.as_view()), name="apps"),
3037
re_path(r"^apps/releases/?$", AppReleaseView.as_view(), name="app-release-create"),
3138
re_path(r"^apps/?$", AppRegisterView.as_view(), name="app-register"),
3239
re_path(r"^apps/(?P<pk>[a-z0-9_]+)/?$", AppView.as_view(), name="app-delete"),
@@ -38,5 +45,9 @@
3845
),
3946
re_path(r"^token/?$", SessionObtainAuthToken.as_view(), name="user-token"),
4047
re_path(r"^token/new/?$", RegenerateAuthToken.as_view(), name="user-token-new"),
41-
re_path(r"^categories.json$", etag(categories_etag)(CategoryView.as_view()), name="category"),
48+
re_path(
49+
r"^categories.json$",
50+
condition(categories_etag, categories_last_modified)(CategoryView.as_view()),
51+
name="category",
52+
),
4253
]

nextcloudappstore/api/v1/views.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
from django.conf import settings
33
from django.db import transaction
44
from django.http import Http404
5+
from django.utils.decorators import method_decorator
6+
from django.views.decorators.gzip import gzip_page
57
from pymple import Container
68
from requests import HTTPError
79
from rest_framework import authentication, parsers, renderers # type: ignore
@@ -66,6 +68,7 @@ class NextcloudReleaseView(ListAPIView):
6668
serializer_class = NextcloudReleaseSerializer
6769

6870

71+
@method_decorator(gzip_page, name="dispatch")
6972
class AppsView(ListAPIView):
7073
queryset = App.objects.prefetch_related(*APP_PREFETCH_LIST).all()
7174
serializer_class = AppSerializer

nextcloudappstore/core/caching.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from typing import Any, List, Tuple
1+
import datetime
2+
from typing import Any, List, Tuple, Union
23

34
from django.db.models import Max, QuerySet
45
from semantic_version import Version
@@ -24,6 +25,12 @@ def create_etag(pairs: List[Tuple[QuerySet, str]]) -> str:
2425
return str(max(result, default=""))
2526

2627

28+
def get_last_modified(pairs: List[Tuple[QuerySet, str]]) -> Union[datetime.datetime, None]:
29+
result = map(lambda p: p[0].aggregate(m=Max(p[1]))["m"], pairs)
30+
result = filter(lambda r: r is not None, result)
31+
return max(result, default=None)
32+
33+
2734
def apps_etag(request: Any, version: str) -> str:
2835
return create_etag(
2936
[
@@ -33,6 +40,15 @@ def apps_etag(request: Any, version: str) -> str:
3340
)
3441

3542

43+
def apps_last_modified(request: Any, version: str) -> Union[datetime.datetime, None]:
44+
return get_last_modified(
45+
[
46+
(App.objects.all(), "last_release"),
47+
(AppReleaseDeleteLog.objects.all(), "last_modified"),
48+
]
49+
)
50+
51+
3652
def apps_all_etag(request: Any) -> str:
3753
return create_etag(
3854
[
@@ -42,6 +58,15 @@ def apps_all_etag(request: Any) -> str:
4258
)
4359

4460

61+
def apps_all_last_modified(request: Any) -> Union[datetime.datetime, None]:
62+
return get_last_modified(
63+
[
64+
(App.objects.all(), "last_release"),
65+
(AppReleaseDeleteLog.objects.all(), "last_modified"),
66+
]
67+
)
68+
69+
4570
def app_etag(request: Any, id: str) -> str:
4671
return str(App.objects.get(id=id).last_modified)
4772

@@ -54,6 +79,10 @@ def categories_etag(request: Any) -> str:
5479
return create_etag([(Category.objects.all(), "last_modified")])
5580

5681

82+
def categories_last_modified(request: Any) -> Union[datetime.datetime, None]:
83+
return get_last_modified([(Category.objects.all(), "last_modified")])
84+
85+
5786
def app_ratings_etag(request: Any) -> str:
5887
return create_etag([(AppRating.objects.all(), "rated_at")])
5988

0 commit comments

Comments
 (0)