Features: 1) Introduced extend_schema for multiple viewsets to improve OpenAPI documentation; 2) Added detailed schema definitions for blog and payments viewsets using drf-spectacular; 3) Transitioned download_digital_asset functionality to class-based DownloadDigitalAssetView for better modularity.
Fixes: 1) Standardized error responses in `DownloadDigitalAssetView`. Extra: Improved maintainability by refactoring serializers and schema definitions into modular components; updated API URLs to use new class-based view.
This commit is contained in:
parent
c9fd4b4f98
commit
c0fcde4bb4
8 changed files with 323 additions and 65 deletions
17
blog/docs/drf/viewsets.py
Normal file
17
blog/docs/drf/viewsets.py
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from drf_spectacular.utils import extend_schema
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
from core.docs.drf import BASE_ERRORS
|
||||||
|
from blog.serializers import PostSerializer
|
||||||
|
|
||||||
|
POST_SCHEMA = {
|
||||||
|
"list": extend_schema(
|
||||||
|
summary=_("list all posts (read-only)"),
|
||||||
|
responses={status.HTTP_200_OK: PostSerializer(many=True), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"retrieve": extend_schema(
|
||||||
|
summary=_("retrieve a single post (read-only)"),
|
||||||
|
responses={status.HTTP_200_OK: PostSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
@ -1,28 +1,30 @@
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
|
from drf_spectacular.utils import extend_schema_view
|
||||||
from rest_framework.viewsets import ReadOnlyModelViewSet
|
from rest_framework.viewsets import ReadOnlyModelViewSet
|
||||||
|
|
||||||
from blog.filters import PostFilter
|
from blog.filters import PostFilter
|
||||||
from blog.models import Post
|
from blog.models import Post
|
||||||
from blog.serializers import PostSerializer
|
from blog.serializers import PostSerializer
|
||||||
|
from blog.docs.drf.viewsets import POST_SCHEMA
|
||||||
from core.permissions import EvibesPermission
|
from core.permissions import EvibesPermission
|
||||||
|
|
||||||
|
|
||||||
|
@extend_schema_view(**POST_SCHEMA)
|
||||||
class PostViewSet(ReadOnlyModelViewSet): # type: ignore [type-arg]
|
class PostViewSet(ReadOnlyModelViewSet): # type: ignore [type-arg]
|
||||||
"""
|
__doc__ = _( # type: ignore [assignment]
|
||||||
Encapsulates operations for managing and retrieving Post entities in a read-only model view set.
|
"Encapsulates operations for managing and retrieving Post entities in a read-only model view set.\n\n"
|
||||||
|
"This class is tailored to handle Post objects that are active and allows filtration based on defined "
|
||||||
This class is tailored to handle Post objects that are active and allows filtration based on defined
|
"filters. It integrates with Django's backend filtering system and ensures operations align with the "
|
||||||
filters. It integrates with Django's backend filtering system and ensures operations align with the
|
"defined permissions. The view set also includes an additional 'retrieve' permission configuration.\n\n"
|
||||||
defined permissions. The view set also includes an additional "retrieve" permission configuration.
|
"Attributes:\n"
|
||||||
|
" serializer_class: Specifies the serializer to be used for Post objects.\n"
|
||||||
Attributes:
|
" permission_classes: Defines the permissions required to interact with this view set.\n"
|
||||||
serializer_class: Specifies the serializer to be used for Post objects.
|
" queryset: Determines the initial queryset, filtered to include only active Post objects.\n"
|
||||||
permission_classes: Defines the permissions required to interact with this view set.
|
" filter_backends: Lists the backends to be used for filtering querysets.\n"
|
||||||
queryset: Determines the initial queryset, filtered to include only active Post objects.
|
" filterset_class: Defines the set of filters used for filtering Post objects.\n"
|
||||||
filter_backends: Lists the backends to be used for filtering querysets.
|
" additional: Contains additional configuration, such as specific action permissions."
|
||||||
filterset_class: Defines the set of filters used for filtering Post objects.
|
)
|
||||||
additional: Contains additional configuration, such as specific action permissions.
|
|
||||||
"""
|
|
||||||
|
|
||||||
serializer_class = PostSerializer
|
serializer_class = PostSerializer
|
||||||
permission_classes = (EvibesPermission,)
|
permission_classes = (EvibesPermission,)
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,11 @@ from core.sitemaps import BrandSitemap, CategorySitemap, ProductSitemap, StaticP
|
||||||
from core.views import (
|
from core.views import (
|
||||||
CacheOperatorView,
|
CacheOperatorView,
|
||||||
ContactUsView,
|
ContactUsView,
|
||||||
|
DownloadDigitalAssetView,
|
||||||
GlobalSearchView,
|
GlobalSearchView,
|
||||||
RequestCursedURLView,
|
RequestCursedURLView,
|
||||||
SupportedLanguagesView,
|
SupportedLanguagesView,
|
||||||
WebsiteParametersView,
|
WebsiteParametersView,
|
||||||
download_digital_asset_view,
|
|
||||||
sitemap_detail,
|
sitemap_detail,
|
||||||
sitemap_index,
|
sitemap_index,
|
||||||
)
|
)
|
||||||
|
|
@ -78,7 +78,7 @@ urlpatterns = [
|
||||||
{"sitemaps": sitemaps},
|
{"sitemaps": sitemaps},
|
||||||
name="sitemap-detail",
|
name="sitemap-detail",
|
||||||
),
|
),
|
||||||
path("download/<str:order_product_uuid>/", download_digital_asset_view, name="download_digital_asset"),
|
path("download/<str:order_product_uuid>/", DownloadDigitalAssetView.as_view(), name="download_digital_asset"),
|
||||||
path("search/", GlobalSearchView.as_view(), name="global_search"),
|
path("search/", GlobalSearchView.as_view(), name="global_search"),
|
||||||
path("app/cache/", CacheOperatorView.as_view(), name="cache_operator"),
|
path("app/cache/", CacheOperatorView.as_view(), name="cache_operator"),
|
||||||
path("app/languages/", SupportedLanguagesView.as_view(), name="supported_languages"),
|
path("app/languages/", SupportedLanguagesView.as_view(), name="supported_languages"),
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ from core.serializers import (
|
||||||
AttributeSimpleSerializer,
|
AttributeSimpleSerializer,
|
||||||
AttributeValueDetailSerializer,
|
AttributeValueDetailSerializer,
|
||||||
AttributeValueSimpleSerializer,
|
AttributeValueSimpleSerializer,
|
||||||
|
BrandDetailSerializer,
|
||||||
|
BrandSimpleSerializer,
|
||||||
BulkAddOrderProductsSerializer,
|
BulkAddOrderProductsSerializer,
|
||||||
BulkAddWishlistProductSerializer,
|
BulkAddWishlistProductSerializer,
|
||||||
BulkRemoveOrderProductsSerializer,
|
BulkRemoveOrderProductsSerializer,
|
||||||
|
|
@ -29,9 +31,21 @@ from core.serializers import (
|
||||||
OrderProductSimpleSerializer,
|
OrderProductSimpleSerializer,
|
||||||
OrderSimpleSerializer,
|
OrderSimpleSerializer,
|
||||||
ProductDetailSerializer,
|
ProductDetailSerializer,
|
||||||
|
ProductImageDetailSerializer,
|
||||||
|
ProductImageSimpleSerializer,
|
||||||
ProductSimpleSerializer,
|
ProductSimpleSerializer,
|
||||||
|
ProductTagDetailSerializer,
|
||||||
|
ProductTagSimpleSerializer,
|
||||||
|
PromoCodeDetailSerializer,
|
||||||
|
PromoCodeSimpleSerializer,
|
||||||
|
PromotionDetailSerializer,
|
||||||
|
PromotionSimpleSerializer,
|
||||||
RemoveOrderProductSerializer,
|
RemoveOrderProductSerializer,
|
||||||
RemoveWishlistProductSerializer,
|
RemoveWishlistProductSerializer,
|
||||||
|
StockDetailSerializer,
|
||||||
|
StockSimpleSerializer,
|
||||||
|
VendorDetailSerializer,
|
||||||
|
VendorSimpleSerializer,
|
||||||
WishlistDetailSerializer,
|
WishlistDetailSerializer,
|
||||||
WishlistSimpleSerializer,
|
WishlistSimpleSerializer,
|
||||||
)
|
)
|
||||||
|
|
@ -652,3 +666,196 @@ ORDER_PRODUCT_SCHEMA = {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BRAND_SCHEMA = {
|
||||||
|
"list": extend_schema(
|
||||||
|
summary=_("list all brands (simple view)"),
|
||||||
|
responses={status.HTTP_200_OK: BrandSimpleSerializer(many=True), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"retrieve": extend_schema(
|
||||||
|
summary=_("retrieve a single brand (detailed view)"),
|
||||||
|
responses={status.HTTP_200_OK: BrandDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"create": extend_schema(
|
||||||
|
summary=_("create a brand"),
|
||||||
|
responses={status.HTTP_201_CREATED: BrandDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"destroy": extend_schema(
|
||||||
|
summary=_("delete a brand"),
|
||||||
|
responses={status.HTTP_204_NO_CONTENT: {}, **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"update": extend_schema(
|
||||||
|
summary=_("rewrite an existing brand saving non-editables"),
|
||||||
|
responses={status.HTTP_200_OK: BrandDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"partial_update": extend_schema(
|
||||||
|
summary=_("rewrite some fields of an existing brand saving non-editables"),
|
||||||
|
responses={status.HTTP_200_OK: BrandDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"seo_meta": extend_schema(
|
||||||
|
summary=_("SEO Meta snapshot for brand"),
|
||||||
|
responses={status.HTTP_200_OK: SeoSnapshotSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
VENDOR_SCHEMA = {
|
||||||
|
"list": extend_schema(
|
||||||
|
summary=_("list all vendors (simple view)"),
|
||||||
|
responses={status.HTTP_200_OK: VendorSimpleSerializer(many=True), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"retrieve": extend_schema(
|
||||||
|
summary=_("retrieve a single vendor (detailed view)"),
|
||||||
|
responses={status.HTTP_200_OK: VendorDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"create": extend_schema(
|
||||||
|
summary=_("create a vendor"),
|
||||||
|
responses={status.HTTP_201_CREATED: VendorDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"destroy": extend_schema(
|
||||||
|
summary=_("delete a vendor"),
|
||||||
|
responses={status.HTTP_204_NO_CONTENT: {}, **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"update": extend_schema(
|
||||||
|
summary=_("rewrite an existing vendor saving non-editables"),
|
||||||
|
responses={status.HTTP_200_OK: VendorDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"partial_update": extend_schema(
|
||||||
|
summary=_("rewrite some fields of an existing vendor saving non-editables"),
|
||||||
|
responses={status.HTTP_200_OK: VendorDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
PRODUCT_IMAGE_SCHEMA = {
|
||||||
|
"list": extend_schema(
|
||||||
|
summary=_("list all product images (simple view)"),
|
||||||
|
responses={status.HTTP_200_OK: ProductImageSimpleSerializer(many=True), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"retrieve": extend_schema(
|
||||||
|
summary=_("retrieve a single product image (detailed view)"),
|
||||||
|
responses={status.HTTP_200_OK: ProductImageDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"create": extend_schema(
|
||||||
|
summary=_("create a product image"),
|
||||||
|
responses={status.HTTP_201_CREATED: ProductImageDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"destroy": extend_schema(
|
||||||
|
summary=_("delete a product image"),
|
||||||
|
responses={status.HTTP_204_NO_CONTENT: {}, **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"update": extend_schema(
|
||||||
|
summary=_("rewrite an existing product image saving non-editables"),
|
||||||
|
responses={status.HTTP_200_OK: ProductImageDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"partial_update": extend_schema(
|
||||||
|
summary=_("rewrite some fields of an existing product image saving non-editables"),
|
||||||
|
responses={status.HTTP_200_OK: ProductImageDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
PROMOCODE_SCHEMA = {
|
||||||
|
"list": extend_schema(
|
||||||
|
summary=_("list all promo codes (simple view)"),
|
||||||
|
responses={status.HTTP_200_OK: PromoCodeSimpleSerializer(many=True), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"retrieve": extend_schema(
|
||||||
|
summary=_("retrieve a single promo code (detailed view)"),
|
||||||
|
responses={status.HTTP_200_OK: PromoCodeDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"create": extend_schema(
|
||||||
|
summary=_("create a promo code"),
|
||||||
|
responses={status.HTTP_201_CREATED: PromoCodeDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"destroy": extend_schema(
|
||||||
|
summary=_("delete a promo code"),
|
||||||
|
responses={status.HTTP_204_NO_CONTENT: {}, **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"update": extend_schema(
|
||||||
|
summary=_("rewrite an existing promo code saving non-editables"),
|
||||||
|
responses={status.HTTP_200_OK: PromoCodeDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"partial_update": extend_schema(
|
||||||
|
summary=_("rewrite some fields of an existing promo code saving non-editables"),
|
||||||
|
responses={status.HTTP_200_OK: PromoCodeDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
PROMOTION_SCHEMA = {
|
||||||
|
"list": extend_schema(
|
||||||
|
summary=_("list all promotions (simple view)"),
|
||||||
|
responses={status.HTTP_200_OK: PromotionSimpleSerializer(many=True), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"retrieve": extend_schema(
|
||||||
|
summary=_("retrieve a single promotion (detailed view)"),
|
||||||
|
responses={status.HTTP_200_OK: PromotionDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"create": extend_schema(
|
||||||
|
summary=_("create a promotion"),
|
||||||
|
responses={status.HTTP_201_CREATED: PromotionDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"destroy": extend_schema(
|
||||||
|
summary=_("delete a promotion"),
|
||||||
|
responses={status.HTTP_204_NO_CONTENT: {}, **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"update": extend_schema(
|
||||||
|
summary=_("rewrite an existing promotion saving non-editables"),
|
||||||
|
responses={status.HTTP_200_OK: PromotionDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"partial_update": extend_schema(
|
||||||
|
summary=_("rewrite some fields of an existing promotion saving non-editables"),
|
||||||
|
responses={status.HTTP_200_OK: PromotionDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
STOCK_SCHEMA = {
|
||||||
|
"list": extend_schema(
|
||||||
|
summary=_("list all stocks (simple view)"),
|
||||||
|
responses={status.HTTP_200_OK: StockSimpleSerializer(many=True), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"retrieve": extend_schema(
|
||||||
|
summary=_("retrieve a single stock (detailed view)"),
|
||||||
|
responses={status.HTTP_200_OK: StockDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"create": extend_schema(
|
||||||
|
summary=_("create a stock record"),
|
||||||
|
responses={status.HTTP_201_CREATED: StockDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"destroy": extend_schema(
|
||||||
|
summary=_("delete a stock record"),
|
||||||
|
responses={status.HTTP_204_NO_CONTENT: {}, **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"update": extend_schema(
|
||||||
|
summary=_("rewrite an existing stock record saving non-editables"),
|
||||||
|
responses={status.HTTP_200_OK: StockDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"partial_update": extend_schema(
|
||||||
|
summary=_("rewrite some fields of an existing stock record saving non-editables"),
|
||||||
|
responses={status.HTTP_200_OK: StockDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
PRODUCT_TAG_SCHEMA = {
|
||||||
|
"list": extend_schema(
|
||||||
|
summary=_("list all product tags (simple view)"),
|
||||||
|
responses={status.HTTP_200_OK: ProductTagSimpleSerializer(many=True), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"retrieve": extend_schema(
|
||||||
|
summary=_("retrieve a single product tag (detailed view)"),
|
||||||
|
responses={status.HTTP_200_OK: ProductTagDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"create": extend_schema(
|
||||||
|
summary=_("create a product tag"),
|
||||||
|
responses={status.HTTP_201_CREATED: ProductTagDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"destroy": extend_schema(
|
||||||
|
summary=_("delete a product tag"),
|
||||||
|
responses={status.HTTP_204_NO_CONTENT: {}, **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"update": extend_schema(
|
||||||
|
summary=_("rewrite an existing product tag saving non-editables"),
|
||||||
|
responses={status.HTTP_200_OK: ProductTagDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"partial_update": extend_schema(
|
||||||
|
summary=_("rewrite some fields of an existing product tag saving non-editables"),
|
||||||
|
responses={status.HTTP_200_OK: ProductTagDetailSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -301,66 +301,64 @@ class BuyAsBusinessView(APIView):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def download_digital_asset_view(request: HttpRequest, *args, **kwargs) -> FileResponse | JsonResponse:
|
class DownloadDigitalAssetView(APIView):
|
||||||
try:
|
__doc__ = _( # type: ignore [assignment]
|
||||||
logger.debug(f"download_digital_asset_view: {kwargs}")
|
"Handles the downloading of a digital asset associated with an order.\n"
|
||||||
op_uuid = str(kwargs.get("order_product_uuid"))
|
"This function attempts to serve the digital asset file located in the "
|
||||||
if not op_uuid:
|
"storage directory of the project. If the file is not found, an HTTP 404 "
|
||||||
raise BadRequest(_("order_product_uuid is required"))
|
"error is raised to indicate the resource is unavailable."
|
||||||
uuid = urlsafe_base64_decode(op_uuid).decode("utf-8")
|
)
|
||||||
|
|
||||||
download = DigitalAssetDownload.objects.get(order_product__uuid=uuid)
|
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse | FileResponse:
|
||||||
|
try:
|
||||||
|
op_uuid = str(kwargs.get("order_product_uuid"))
|
||||||
|
if not op_uuid:
|
||||||
|
raise BadRequest(_("order_product_uuid is required"))
|
||||||
|
uuid = urlsafe_base64_decode(op_uuid).decode("utf-8")
|
||||||
|
|
||||||
if download.num_downloads >= 1:
|
download = DigitalAssetDownload.objects.get(order_product__uuid=uuid)
|
||||||
raise BadRequest(_("you can only download the digital asset once"))
|
|
||||||
|
|
||||||
if download.order_product.status != "FINISHED":
|
if download.num_downloads >= 1:
|
||||||
raise BadRequest(_("the order must be paid before downloading the digital asset"))
|
raise BadRequest(_("you can only download the digital asset once"))
|
||||||
|
|
||||||
download.num_downloads += 1
|
if download.order_product.status != "FINISHED":
|
||||||
download.save()
|
raise BadRequest(_("the order must be paid before downloading the digital asset"))
|
||||||
|
|
||||||
if not download.order_product.product:
|
download.num_downloads += 1
|
||||||
raise BadRequest(_("the order product does not have a product"))
|
download.save()
|
||||||
|
|
||||||
file_path = download.order_product.product.stocks.first().digital_asset.path # type: ignore [union-attr]
|
if not download.order_product.product:
|
||||||
|
raise BadRequest(_("the order product does not have a product"))
|
||||||
|
|
||||||
content_type, encoding = mimetypes.guess_type(file_path)
|
file_path = download.order_product.product.stocks.first().digital_asset.path # type: ignore [union-attr]
|
||||||
if not content_type:
|
|
||||||
content_type = "application/octet-stream"
|
|
||||||
|
|
||||||
response = FileResponse(open(file_path, "rb"), content_type=content_type)
|
content_type, encoding = mimetypes.guess_type(file_path)
|
||||||
filename = os.path.basename(file_path)
|
if not content_type:
|
||||||
response["Content-Disposition"] = f'attachment; filename="{filename}"'
|
content_type = "application/octet-stream"
|
||||||
return response
|
|
||||||
|
|
||||||
except BadRequest as e:
|
response = FileResponse(open(file_path, "rb"), content_type=content_type)
|
||||||
return JsonResponse(camelize({"error": str(e)}), status=400)
|
filename = os.path.basename(file_path)
|
||||||
|
response["Content-Disposition"] = f'attachment; filename="{filename}"'
|
||||||
|
return response
|
||||||
|
|
||||||
except DigitalAssetDownload.DoesNotExist:
|
except BadRequest as e:
|
||||||
return JsonResponse(camelize({"error": "Digital asset not found"}), status=404)
|
return Response(data=camelize({"error": str(e)}), status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
except Exception as e:
|
except DigitalAssetDownload.DoesNotExist:
|
||||||
capture_exception(e)
|
return Response(data=camelize({"error": "Digital asset not found"}), status=status.HTTP_404_NOT_FOUND)
|
||||||
return JsonResponse(
|
|
||||||
camelize(
|
|
||||||
{
|
|
||||||
"error": "An error occurred while trying to download the digital asset",
|
|
||||||
"traceback": traceback.format_exc() if settings.DEBUG else None,
|
|
||||||
"received": {"order_product_uuid": kwargs.get("order_product_uuid", "")},
|
|
||||||
}
|
|
||||||
),
|
|
||||||
status=500,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
# noinspection PyTypeChecker
|
capture_exception(e)
|
||||||
download_digital_asset_view.__doc__ = _( # type: ignore [assignment]
|
return Response(
|
||||||
"Handles the downloading of a digital asset associated with an order.\n"
|
data=camelize(
|
||||||
"This function attempts to serve the digital asset file located in the "
|
{
|
||||||
"storage directory of the project. If the file is not found, an HTTP 404 "
|
"error": "An error occurred while trying to download the digital asset",
|
||||||
"error is raised to indicate the resource is unavailable."
|
"traceback": traceback.format_exc() if settings.DEBUG else None,
|
||||||
)
|
"received": {"order_product_uuid": kwargs.get("order_product_uuid", "")},
|
||||||
|
}
|
||||||
|
),
|
||||||
|
status=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def favicon_view(request: HttpRequest, *args, **kwargs) -> HttpResponse | FileResponse:
|
def favicon_view(request: HttpRequest, *args, **kwargs) -> HttpResponse | FileResponse:
|
||||||
|
|
|
||||||
|
|
@ -31,11 +31,18 @@ from core.docs.drf.viewsets import (
|
||||||
ATTRIBUTE_GROUP_SCHEMA,
|
ATTRIBUTE_GROUP_SCHEMA,
|
||||||
ATTRIBUTE_SCHEMA,
|
ATTRIBUTE_SCHEMA,
|
||||||
ATTRIBUTE_VALUE_SCHEMA,
|
ATTRIBUTE_VALUE_SCHEMA,
|
||||||
|
BRAND_SCHEMA,
|
||||||
CATEGORY_SCHEMA,
|
CATEGORY_SCHEMA,
|
||||||
FEEDBACK_SCHEMA,
|
FEEDBACK_SCHEMA,
|
||||||
ORDER_PRODUCT_SCHEMA,
|
ORDER_PRODUCT_SCHEMA,
|
||||||
ORDER_SCHEMA,
|
ORDER_SCHEMA,
|
||||||
|
PRODUCT_IMAGE_SCHEMA,
|
||||||
PRODUCT_SCHEMA,
|
PRODUCT_SCHEMA,
|
||||||
|
PRODUCT_TAG_SCHEMA,
|
||||||
|
PROMOCODE_SCHEMA,
|
||||||
|
PROMOTION_SCHEMA,
|
||||||
|
STOCK_SCHEMA,
|
||||||
|
VENDOR_SCHEMA,
|
||||||
WISHLIST_SCHEMA,
|
WISHLIST_SCHEMA,
|
||||||
)
|
)
|
||||||
from core.filters import AddressFilter, BrandFilter, CategoryFilter, FeedbackFilter, OrderFilter, ProductFilter
|
from core.filters import AddressFilter, BrandFilter, CategoryFilter, FeedbackFilter, OrderFilter, ProductFilter
|
||||||
|
|
@ -313,6 +320,7 @@ class CategoryViewSet(EvibesViewSet):
|
||||||
return Response(SeoSnapshotSerializer(payload).data)
|
return Response(SeoSnapshotSerializer(payload).data)
|
||||||
|
|
||||||
|
|
||||||
|
@extend_schema_view(**BRAND_SCHEMA)
|
||||||
class BrandViewSet(EvibesViewSet):
|
class BrandViewSet(EvibesViewSet):
|
||||||
__doc__ = _(
|
__doc__ = _(
|
||||||
"Represents a viewset for managing Brand instances. "
|
"Represents a viewset for managing Brand instances. "
|
||||||
|
|
@ -554,6 +562,7 @@ class ProductViewSet(EvibesViewSet):
|
||||||
return Response(SeoSnapshotSerializer(payload).data)
|
return Response(SeoSnapshotSerializer(payload).data)
|
||||||
|
|
||||||
|
|
||||||
|
@extend_schema_view(**VENDOR_SCHEMA)
|
||||||
class VendorViewSet(EvibesViewSet):
|
class VendorViewSet(EvibesViewSet):
|
||||||
__doc__ = _(
|
__doc__ = _(
|
||||||
"Represents a viewset for managing Vendor objects. "
|
"Represents a viewset for managing Vendor objects. "
|
||||||
|
|
@ -853,6 +862,7 @@ class OrderProductViewSet(EvibesViewSet):
|
||||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
return Response(status=status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
|
||||||
|
@extend_schema_view(**PRODUCT_IMAGE_SCHEMA)
|
||||||
class ProductImageViewSet(EvibesViewSet):
|
class ProductImageViewSet(EvibesViewSet):
|
||||||
__doc__ = _("Manages operations related to Product images in the application. ")
|
__doc__ = _("Manages operations related to Product images in the application. ")
|
||||||
|
|
||||||
|
|
@ -865,6 +875,7 @@ class ProductImageViewSet(EvibesViewSet):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@extend_schema_view(**PROMOCODE_SCHEMA)
|
||||||
class PromoCodeViewSet(EvibesViewSet):
|
class PromoCodeViewSet(EvibesViewSet):
|
||||||
__doc__ = _("Manages the retrieval and handling of PromoCode instances through various API actions.")
|
__doc__ = _("Manages the retrieval and handling of PromoCode instances through various API actions.")
|
||||||
|
|
||||||
|
|
@ -886,6 +897,7 @@ class PromoCodeViewSet(EvibesViewSet):
|
||||||
return qs.filter(user=user)
|
return qs.filter(user=user)
|
||||||
|
|
||||||
|
|
||||||
|
@extend_schema_view(**PROMOTION_SCHEMA)
|
||||||
class PromotionViewSet(EvibesViewSet):
|
class PromotionViewSet(EvibesViewSet):
|
||||||
__doc__ = _("Represents a view set for managing promotions. ")
|
__doc__ = _("Represents a view set for managing promotions. ")
|
||||||
|
|
||||||
|
|
@ -898,6 +910,7 @@ class PromotionViewSet(EvibesViewSet):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@extend_schema_view(**STOCK_SCHEMA)
|
||||||
class StockViewSet(EvibesViewSet):
|
class StockViewSet(EvibesViewSet):
|
||||||
__doc__ = _("Handles operations related to Stock data in the system.")
|
__doc__ = _("Handles operations related to Stock data in the system.")
|
||||||
|
|
||||||
|
|
@ -1100,6 +1113,7 @@ class AddressViewSet(EvibesViewSet):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@extend_schema_view(**PRODUCT_TAG_SCHEMA)
|
||||||
class ProductTagViewSet(EvibesViewSet):
|
class ProductTagViewSet(EvibesViewSet):
|
||||||
__doc__ = _(
|
__doc__ = _(
|
||||||
"Handles operations related to Product Tags within the application. "
|
"Handles operations related to Product Tags within the application. "
|
||||||
|
|
|
||||||
17
payments/docs/drf/viewsets.py
Normal file
17
payments/docs/drf/viewsets.py
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from drf_spectacular.utils import extend_schema
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
from core.docs.drf import BASE_ERRORS
|
||||||
|
from payments.serializers import TransactionSerializer
|
||||||
|
|
||||||
|
TRANSACTION_SCHEMA = {
|
||||||
|
"list": extend_schema(
|
||||||
|
summary=_("list all transactions (read-only)"),
|
||||||
|
responses={status.HTTP_200_OK: TransactionSerializer(many=True), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
"retrieve": extend_schema(
|
||||||
|
summary=_("retrieve a single transaction (read-only)"),
|
||||||
|
responses={status.HTTP_200_OK: TransactionSerializer(), **BASE_ERRORS},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from drf_spectacular.utils import extend_schema_view
|
||||||
from rest_framework.viewsets import ReadOnlyModelViewSet
|
from rest_framework.viewsets import ReadOnlyModelViewSet
|
||||||
|
|
||||||
from core.permissions import EvibesPermission, IsOwner
|
from core.permissions import EvibesPermission, IsOwner
|
||||||
from payments.serializers import TransactionSerializer
|
from payments.serializers import TransactionSerializer
|
||||||
|
from payments.docs.drf.viewsets import TRANSACTION_SCHEMA
|
||||||
|
|
||||||
|
|
||||||
|
@extend_schema_view(**TRANSACTION_SCHEMA)
|
||||||
class TransactionViewSet(ReadOnlyModelViewSet): # type: ignore
|
class TransactionViewSet(ReadOnlyModelViewSet): # type: ignore
|
||||||
__doc__ = _( # type: ignore [assignment]
|
__doc__ = _( # type: ignore [assignment]
|
||||||
"ViewSet for handling read-only operations on the Transaction model. "
|
"ViewSet for handling read-only operations on the Transaction model. "
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue