mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
dev: mini cache framework and caching for users and instance configuration
This commit is contained in:
parent
453d4d9e3e
commit
ff78ef8f61
@ -12,6 +12,7 @@ from rest_framework.response import Response
|
||||
# Module imports
|
||||
from .base import BaseAPIView
|
||||
from plane.license.utils.instance_value import get_configuration_value
|
||||
from ...utils.cache import cache_path_response
|
||||
|
||||
|
||||
class ConfigurationEndpoint(BaseAPIView):
|
||||
@ -19,6 +20,7 @@ class ConfigurationEndpoint(BaseAPIView):
|
||||
AllowAny,
|
||||
]
|
||||
|
||||
@cache_path_response(60 * 60 * 2)
|
||||
def get(self, request):
|
||||
# Get all the configuration
|
||||
(
|
||||
@ -136,6 +138,7 @@ class MobileConfigurationEndpoint(BaseAPIView):
|
||||
AllowAny,
|
||||
]
|
||||
|
||||
@cache_path_response(60 * 60 * 2)
|
||||
def get(self, request):
|
||||
(
|
||||
GOOGLE_CLIENT_ID,
|
||||
|
@ -1,8 +1,9 @@
|
||||
# Django imports
|
||||
from django.db.models import Q, F, Count, Case, When, IntegerField
|
||||
# Third party imports
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
|
||||
|
||||
# Module imports
|
||||
from plane.app.serializers import (
|
||||
UserSerializer,
|
||||
@ -15,10 +16,7 @@ from plane.app.views.base import BaseViewSet, BaseAPIView
|
||||
from plane.db.models import User, IssueActivity, WorkspaceMember, ProjectMember
|
||||
from plane.license.models import Instance, InstanceAdmin
|
||||
from plane.utils.paginator import BasePaginator
|
||||
|
||||
|
||||
from django.db.models import Q, F, Count, Case, When, IntegerField
|
||||
|
||||
from ...utils.cache import cache_user_response, invalidate_user_cache
|
||||
|
||||
class UserEndpoint(BaseViewSet):
|
||||
serializer_class = UserSerializer
|
||||
@ -27,6 +25,7 @@ class UserEndpoint(BaseViewSet):
|
||||
def get_object(self):
|
||||
return self.request.user
|
||||
|
||||
@cache_user_response(60*15)
|
||||
def retrieve(self, request):
|
||||
serialized_data = UserMeSerializer(request.user).data
|
||||
return Response(
|
||||
@ -34,10 +33,12 @@ class UserEndpoint(BaseViewSet):
|
||||
status=status.HTTP_200_OK,
|
||||
)
|
||||
|
||||
@cache_user_response(60*15)
|
||||
def retrieve_user_settings(self, request):
|
||||
serialized_data = UserMeSettingsSerializer(request.user).data
|
||||
return Response(serialized_data, status=status.HTTP_200_OK)
|
||||
|
||||
@cache_user_response(60*15)
|
||||
def retrieve_instance_admin(self, request):
|
||||
instance = Instance.objects.first()
|
||||
is_admin = InstanceAdmin.objects.filter(
|
||||
@ -47,6 +48,11 @@ class UserEndpoint(BaseViewSet):
|
||||
{"is_instance_admin": is_admin}, status=status.HTTP_200_OK
|
||||
)
|
||||
|
||||
@invalidate_user_cache("/api/users/me/")
|
||||
def partial_update(self, request, *args, **kwargs):
|
||||
return super().partial_update(request, *args, **kwargs)
|
||||
|
||||
@invalidate_user_cache("/api/users/me/")
|
||||
def deactivate(self, request):
|
||||
# Check all workspace user is active
|
||||
user = self.get_object()
|
||||
@ -145,6 +151,8 @@ class UserEndpoint(BaseViewSet):
|
||||
|
||||
|
||||
class UpdateUserOnBoardedEndpoint(BaseAPIView):
|
||||
|
||||
@invalidate_user_cache("/api/users/me")
|
||||
def patch(self, request):
|
||||
user = User.objects.get(pk=request.user.id, is_active=True)
|
||||
user.is_onboarded = request.data.get("is_onboarded", False)
|
||||
@ -155,6 +163,8 @@ class UpdateUserOnBoardedEndpoint(BaseAPIView):
|
||||
|
||||
|
||||
class UpdateUserTourCompletedEndpoint(BaseAPIView):
|
||||
|
||||
@invalidate_user_cache("/api/users/me")
|
||||
def patch(self, request):
|
||||
user = User.objects.get(pk=request.user.id, is_active=True)
|
||||
user.is_tour_completed = request.data.get("is_tour_completed", False)
|
||||
|
@ -32,6 +32,7 @@ from plane.license.api.permissions import (
|
||||
)
|
||||
from plane.db.models import User, WorkspaceMember, ProjectMember
|
||||
from plane.license.utils.encryption import encrypt_data
|
||||
from plane.utils.cache import cache_path_response, invalidate_path_cache
|
||||
|
||||
|
||||
class InstanceEndpoint(BaseAPIView):
|
||||
@ -44,6 +45,7 @@ class InstanceEndpoint(BaseAPIView):
|
||||
AllowAny(),
|
||||
]
|
||||
|
||||
@cache_path_response(60 * 60 * 2)
|
||||
def get(self, request):
|
||||
instance = Instance.objects.first()
|
||||
# get the instance
|
||||
@ -58,6 +60,7 @@ class InstanceEndpoint(BaseAPIView):
|
||||
data["is_activated"] = True
|
||||
return Response(data, status=status.HTTP_200_OK)
|
||||
|
||||
@invalidate_path_cache
|
||||
def patch(self, request):
|
||||
# Get the instance
|
||||
instance = Instance.objects.first()
|
||||
@ -104,6 +107,7 @@ class InstanceAdminEndpoint(BaseAPIView):
|
||||
serializer = InstanceAdminSerializer(instance_admin)
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
|
||||
@invalidate_path_cache("/api/instances/")
|
||||
def get(self, request):
|
||||
instance = Instance.objects.first()
|
||||
if instance is None:
|
||||
@ -115,6 +119,7 @@ class InstanceAdminEndpoint(BaseAPIView):
|
||||
serializer = InstanceAdminSerializer(instance_admins, many=True)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
@invalidate_path_cache("/api/instances/")
|
||||
def delete(self, request, pk):
|
||||
instance = Instance.objects.first()
|
||||
instance_admin = InstanceAdmin.objects.filter(
|
||||
@ -135,6 +140,8 @@ class InstanceConfigurationEndpoint(BaseAPIView):
|
||||
)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
@invalidate_path_cache("/api/configs/")
|
||||
@invalidate_path_cache("/api/mobile-configs/")
|
||||
def patch(self, request):
|
||||
configurations = InstanceConfiguration.objects.filter(
|
||||
key__in=request.data.keys()
|
||||
@ -170,6 +177,7 @@ class InstanceAdminSignInEndpoint(BaseAPIView):
|
||||
AllowAny,
|
||||
]
|
||||
|
||||
@invalidate_path_cache("/api/instances/")
|
||||
def post(self, request):
|
||||
# Check instance first
|
||||
instance = Instance.objects.first()
|
||||
@ -260,6 +268,7 @@ class SignUpScreenVisitedEndpoint(BaseAPIView):
|
||||
AllowAny,
|
||||
]
|
||||
|
||||
@invalidate_path_cache("/api/instances/")
|
||||
def post(self, request):
|
||||
instance = Instance.objects.first()
|
||||
if instance is None:
|
||||
|
93
apiserver/plane/utils/cache.py
Normal file
93
apiserver/plane/utils/cache.py
Normal file
@ -0,0 +1,93 @@
|
||||
from django.core.cache import cache
|
||||
from django.utils.encoding import force_bytes
|
||||
import hashlib
|
||||
from functools import wraps
|
||||
from rest_framework.response import Response
|
||||
from datetime import datetime, timedelta
|
||||
from django.utils.http import http_date
|
||||
|
||||
|
||||
def generate_cache_key(custom_path, auth_header=None):
|
||||
if auth_header:
|
||||
key_data = f'{custom_path}:{auth_header}'
|
||||
else:
|
||||
key_data = custom_path
|
||||
return hashlib.md5(force_bytes(key_data)).hexdigest()
|
||||
|
||||
def cache_user_response(timeout, path=None):
|
||||
def decorator(view_func):
|
||||
@wraps(view_func)
|
||||
def _wrapped_view(instance, request, *args, **kwargs):
|
||||
# Function to generate cache key
|
||||
auth_header = request.META.get('HTTP_AUTHORIZATION', '')
|
||||
custom_path = path if path is not None else request.get_full_path()
|
||||
key = generate_cache_key(custom_path, auth_header)
|
||||
cached_result = cache.get(key)
|
||||
if cached_result is not None:
|
||||
return Response(cached_result['data'], status=cached_result['status'])
|
||||
|
||||
response = view_func(instance, request, *args, **kwargs)
|
||||
|
||||
if response.status_code == 200:
|
||||
cache.set(key, {'data': response.data, 'status': response.status_code}, timeout)
|
||||
response['Cache-Control'] = f'max-age={timeout}'
|
||||
expires_time = datetime.utcnow() + timedelta(seconds=timeout)
|
||||
response['Expires'] = http_date(expires_time.timestamp())
|
||||
|
||||
return response
|
||||
return _wrapped_view
|
||||
return decorator
|
||||
|
||||
def invalidate_user_cache(path):
|
||||
def decorator(view_func):
|
||||
@wraps(view_func)
|
||||
def _wrapped_view(instance, request, *args, **kwargs):
|
||||
# Invalidate cache before executing the view function
|
||||
custom_path = path if path is not None else request.get_full_path()
|
||||
auth_header = request.META.get('HTTP_AUTHORIZATION', '')
|
||||
key = generate_cache_key(custom_path, auth_header)
|
||||
cache.delete(key)
|
||||
|
||||
# Execute the view function
|
||||
return view_func(instance, request, *args, **kwargs)
|
||||
return _wrapped_view
|
||||
return decorator
|
||||
|
||||
|
||||
def cache_path_response(timeout, path=None):
|
||||
def decorator(view_func):
|
||||
@wraps(view_func)
|
||||
def _wrapped_view(instance, request, *args, **kwargs):
|
||||
# Function to generate cache key
|
||||
custom_path = path if path is not None else request.get_full_path()
|
||||
key = generate_cache_key(custom_path, None)
|
||||
cached_result = cache.get(key)
|
||||
if cached_result is not None:
|
||||
return Response(cached_result['data'], status=cached_result['status'])
|
||||
|
||||
response = view_func(instance, request, *args, **kwargs)
|
||||
|
||||
if response.status_code == 200:
|
||||
cache.set(key, {'data': response.data, 'status': response.status_code}, timeout)
|
||||
response['Cache-Control'] = f'max-age={timeout}'
|
||||
expires_time = datetime.utcnow() + timedelta(seconds=timeout)
|
||||
response['Expires'] = http_date(expires_time.timestamp())
|
||||
|
||||
return response
|
||||
return _wrapped_view
|
||||
return decorator
|
||||
|
||||
def invalidate_path_cache(path=None):
|
||||
def decorator(view_func):
|
||||
@wraps(view_func)
|
||||
def _wrapped_view(instance, request, *args, **kwargs):
|
||||
# Invalidate cache before executing the view function
|
||||
custom_path = path if path is not None else request.get_full_path()
|
||||
key = generate_cache_key(custom_path, None)
|
||||
cache.delete(key)
|
||||
|
||||
# Execute the view function
|
||||
return view_func(instance, request, *args, **kwargs)
|
||||
return _wrapped_view
|
||||
return decorator
|
||||
|
Loading…
Reference in New Issue
Block a user