Skip to content

Commit

Permalink
Added AssetPublicQRViewSet to handle QR code based asset retrieval (#…
Browse files Browse the repository at this point in the history
…1625)

* Add public asset endpoint by qr_code_id

* Add tests

* use get_object_or_404

* add signals and tests

* manual cache test

---------

Co-authored-by: Aakash Singh <[email protected]>
  • Loading branch information
Ashesh3 and sainak authored Sep 26, 2023
1 parent 523a6f9 commit 4106fe6
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 2 deletions.
41 changes: 41 additions & 0 deletions care/facility/api/viewsets/asset.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import uuid

from django.conf import settings
from django.core.cache import cache
from django.db.models import Exists, OuterRef, Q
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.http import Http404
from django.shortcuts import get_object_or_404
from django.utils import timezone
Expand Down Expand Up @@ -58,6 +62,13 @@
inverse_asset_status = inverse_choices(StatusChoices)


@receiver(post_save, sender=Asset)
def delete_asset_cache(sender, instance, created, **kwargs):
cache.delete("asset:" + str(instance.external_id))
cache.delete("asset:qr:" + str(instance.qr_code_id))
cache.delete("asset:qr:" + str(instance.id))


class AssetLocationViewSet(
ListModelMixin,
RetrieveModelMixin,
Expand Down Expand Up @@ -175,6 +186,36 @@ def retrieve(self, request, *args, **kwargs):
return Response(hit)


class AssetPublicQRViewSet(GenericViewSet):
queryset = Asset.objects.all()
serializer_class = AssetSerializer
lookup_field = "qr_code_id"

def retrieve(self, request, *args, **kwargs):
is_uuid = True
try:
uuid.UUID(kwargs["qr_code_id"])
except ValueError:
# If the qr_code_id is not a UUID, then it is the pk of the asset
is_uuid = False
if not kwargs["qr_code_id"].isnumeric():
return Response(status=status.HTTP_404_NOT_FOUND)

key = "asset:qr:" + kwargs["qr_code_id"]
hit = cache.get(key)
if not hit:
if is_uuid:
instance = self.get_object()
else:
instance = get_object_or_404(
self.get_queryset(), pk=kwargs["qr_code_id"]
)
serializer = self.get_serializer(instance)
cache.set(key, serializer.data, 60 * 60 * 24)
return Response(serializer.data)
return Response(hit)


class AssetAvailabilityFilter(filters.FilterSet):
external_id = filters.CharFilter(field_name="asset__external_id")

Expand Down
49 changes: 48 additions & 1 deletion care/facility/tests/test_asset_public_api.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from django.core.cache import cache
from rest_framework import status
from rest_framework.test import APITestCase

from care.utils.tests.test_utils import TestUtils
from care.facility.api.serializers.asset import AssetSerializer
from care.utils.tests.test_utils import TestUtils, override_cache


class AssetPublicViewSetTestCase(TestUtils, APITestCase):
Expand All @@ -23,3 +25,48 @@ def test_retrieve_asset(self):
def test_retrieve_nonexistent_asset(self):
response = self.client.get("/api/v1/public/asset/nonexistent/")
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

def test_retrieve_asset_qr_code(self):
response = self.client.get(f"/api/v1/public/asset_qr/{self.asset.qr_code_id}/")
self.assertEqual(response.status_code, status.HTTP_200_OK)

response = self.client.get(f"/api/v1/public/asset_qr/{self.asset.id}/")
self.assertEqual(response.status_code, status.HTTP_200_OK)

def test_retrieve_nonexistent_asset_qr_code(self):
response = self.client.get("/api/v1/public/asset_qr/nonexistent/")
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

def test_retrieve_asset_qr_cached(self):
with override_cache(self):
response = self.client.get(
f"/api/v1/public/asset_qr/{self.asset.qr_code_id}/"
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["name"], self.asset.name)

# Update the asset to invalidate the cache

updated_data = {
"name": "New Updated Test Asset",
}
response = self.client.patch(
f"/api/v1/asset/{self.asset.external_id}/", updated_data
)
self.assertEqual(response.status_code, status.HTTP_200_OK)

response = self.client.get(
f"/api/v1/public/asset_qr/{self.asset.qr_code_id}/"
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["name"], updated_data["name"])

def test_retrieve_asset_qr_pre_cached(self):
with override_cache(self):
serializer = AssetSerializer(self.asset)
cache.set(f"asset:qr:{self.asset.qr_code_id}", serializer.data)
response = self.client.get(
f"/api/v1/public/asset_qr/{self.asset.qr_code_id}/"
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["name"], self.asset.name)
3 changes: 2 additions & 1 deletion care/utils/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,8 @@ def create_asset(cls, location: AssetLocation, **kwargs) -> Asset:
"name": "Test Asset",
"current_location": location,
"asset_type": 50,
"warranty_amc_end_of_validity": make_aware(datetime(2030, 4, 1)),
"warranty_amc_end_of_validity": make_aware(datetime(2030, 4, 1)).date(),
"qr_code_id": "3dcee5fa-8fb8-4b07-be12-8e0d0baf6692",
}
data.update(kwargs)
return Asset.objects.create(**data)
Expand Down
2 changes: 2 additions & 0 deletions config/api_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from care.facility.api.viewsets.asset import (
AssetAvailabilityViewSet,
AssetLocationViewSet,
AssetPublicQRViewSet,
AssetPublicViewSet,
AssetServiceViewSet,
AssetTransactionViewSet,
Expand Down Expand Up @@ -219,6 +220,7 @@

# Public endpoints
router.register("public/asset", AssetPublicViewSet)
router.register("public/asset_qr", AssetPublicQRViewSet)

# ABDM endpoints
if settings.ENABLE_ABDM:
Expand Down

0 comments on commit 4106fe6

Please sign in to comment.