From 89f6594751067774cab3822e11df99d95b6013d9 Mon Sep 17 00:00:00 2001 From: Egor fureunoir Gorbunov Date: Mon, 26 May 2025 14:54:21 +0300 Subject: [PATCH] Features: 1) Add `RecentProductConnection` to support recently viewed products in GraphQL; 2) Implement `recently_viewed` field in `UserType` with reverse-chronological product ordering; 3) Add `recently_viewed` field to `UserSerializer` and return data with `ProductSimpleSerializer`. Fixes: 1) Fix `resolve_recently_viewed` to handle empty UUIDs and avoid breaking queries. Extra: Refactor imports in `graphene/object_types.py` and `serializers.py` for better clarity; Adjust minor formatting in `TokenObtainSerializer`. --- vibes_auth/graphene/object_types.py | 33 +++++++++++++++++++++++++---- vibes_auth/serializers.py | 10 +++++++-- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/vibes_auth/graphene/object_types.py b/vibes_auth/graphene/object_types.py index f265fe84..4ef5ff6f 100644 --- a/vibes_auth/graphene/object_types.py +++ b/vibes_auth/graphene/object_types.py @@ -3,8 +3,10 @@ from django.utils.translation import gettext_lazy as _ from graphene import Field, List, String, relay from graphene.types.generic import GenericScalar from graphene_django import DjangoObjectType +from graphql_relay.connection.array_connection import connection_from_array -from core.graphene.object_types import OrderType, WishlistType +from core.graphene.object_types import OrderType, ProductType, WishlistType +from core.models import Product from evibes.settings import LANGUAGE_CODE, LANGUAGES from payments.graphene.object_types import BalanceType from vibes_auth.models import User @@ -26,8 +28,16 @@ class PermissionType(DjangoObjectType): filter_fields = ["name", "id"] +class RecentProductConnection(relay.Connection): + class Meta: + node = ProductType + + class UserType(DjangoObjectType): - recently_viewed = GenericScalar(description=_("recently viewed products' UUIDs")) + recently_viewed = relay.ConnectionField( + RecentProductConnection, + description=_("the products this user has viewed most recently (max 48), in reverse‐chronological order"), + ) groups = List(lambda: GroupType, description=_("groups")) user_permissions = List(lambda: PermissionType, description=_("permissions")) orders = List(lambda: OrderType, description=_("orders")) @@ -89,8 +99,23 @@ class UserType(DjangoObjectType): def resolve_orders(self, info): return self.orders.all() if self.orders.count() >= 1 else [] - def resolve_recently_viewed(self, info): - return [] or self.recently_viewed + def resolve_recently_viewed(self, info, **kwargs): + uuid_list = self.recently_viewed or [] + + if not uuid_list: + return connection_from_array([], kwargs) + + qs = Product.objects.filter(uuid__in=uuid_list) + + products_by_uuid = {str(p.uuid): p for p in qs} + + ordered_products = [ + products_by_uuid[u] + for u in uuid_list + if u in products_by_uuid + ] + + return connection_from_array(ordered_products, kwargs) def resolve_groups(self, info): return self.groups.all() if self.groups.count() >= 1 else [] diff --git a/vibes_auth/serializers.py b/vibes_auth/serializers.py index 0b79fa1c..d881485f 100644 --- a/vibes_auth/serializers.py +++ b/vibes_auth/serializers.py @@ -21,6 +21,8 @@ from rest_framework_simplejwt.settings import api_settings from rest_framework_simplejwt.token_blacklist.models import BlacklistedToken from rest_framework_simplejwt.tokens import RefreshToken, Token, UntypedToken +from core.models import Product +from core.serializers import ProductSimpleSerializer from core.utils.security import is_safe_key from evibes import settings from vibes_auth.models import User @@ -32,6 +34,7 @@ class UserSerializer(ModelSerializer): avatar_url = SerializerMethodField(required=False, read_only=True) password = CharField(write_only=True, required=False) is_staff = BooleanField(read_only=True) + recently_viewed = SerializerMethodField(required=False, read_only=True) @staticmethod def get_avatar_url(obj) -> str: @@ -84,6 +87,9 @@ class UserSerializer(ModelSerializer): validate_password(attrs["password"]) return attrs + def get_recently_viewed(self, obj) -> ProductSimpleSerializer.data: + return ProductSimpleSerializer(Product.objects.filter(uuid__in=([] or obj.recently_viewed)), many=True).data + class TokenObtainSerializer(Serializer): username_field = User.USERNAME_FIELD @@ -177,8 +183,8 @@ class TokenVerifySerializer(Serializer): token = UntypedToken(attrs["token"]) if ( - api_settings.BLACKLIST_AFTER_ROTATION - and "rest_framework_simplejwt.token_blacklist" in settings.INSTALLED_APPS + api_settings.BLACKLIST_AFTER_ROTATION + and "rest_framework_simplejwt.token_blacklist" in settings.INSTALLED_APPS ): jti = token.get(api_settings.JTI_CLAIM) if BlacklistedToken.objects.filter(token__jti=jti).exists():