diff --git a/backend/core/api/public/decorators.py b/backend/core/api/public/decorators.py
index 854a8e2b4..71360540c 100644
--- a/backend/core/api/public/decorators.py
+++ b/backend/core/api/public/decorators.py
@@ -2,10 +2,10 @@
from rest_framework.exceptions import PermissionDenied
from rest_framework.generics import get_object_or_404
-from rest_framework.response import Response
from rest_framework import status
from backend.models import TeamMemberPermission, Organization, Client
+from backend.core.api.public.helpers.response import APIResponse
import logging
@@ -21,21 +21,21 @@ def _wrapped_view(request, *args, **kwargs):
logger.info(
f"Authentication credentials were not provided in api request |" f" {request.META.get('REMOTE_ADDR', 'Unknown IP')}"
)
- return Response({"detail": "Authentication credentials were not provided."}, status=status.HTTP_401_UNAUTHORIZED)
+ return APIResponse(False, {"detail": "Authentication credentials were not provided."}, status=status.HTTP_401_UNAUTHORIZED)
if request.team_id and not request.team:
- return Response({"detail": "Team not found."}, status=status.HTTP_404_NOT_FOUND)
+ return APIResponse(False, {"detail": "Team not found."}, status=status.HTTP_404_NOT_FOUND)
if request.team:
# Check for team permissions based on team_id and scopes
if not request.team.is_owner(token.user) and not request.team.is_logged_in_as_team(request):
team_permissions = TeamMemberPermission.objects.filter(team=request.team, user=token.user).first()
if not team_permissions or not all(scope in team_permissions.scopes for scope in scopes):
- return Response({"detail": "Permission denied."}, status=status.HTTP_403_FORBIDDEN)
+ return APIResponse(False, {"detail": "Permission denied."}, status=status.HTTP_403_FORBIDDEN)
# Check for global API Key permissions based on token scopes
if not all(scope in token.scopes for scope in scopes):
- return Response({"detail": "Permission denied."}, status=status.HTTP_403_FORBIDDEN)
+ return APIResponse(False, {"detail": "Permission denied."}, status=status.HTTP_403_FORBIDDEN)
token.update_last_used()
diff --git a/backend/core/api/public/endpoints/Invoices/delete.py b/backend/core/api/public/endpoints/Invoices/delete.py
index f2f3f0b49..6f7ed1770 100644
--- a/backend/core/api/public/endpoints/Invoices/delete.py
+++ b/backend/core/api/public/endpoints/Invoices/delete.py
@@ -1,10 +1,11 @@
from django.http import QueryDict
from rest_framework import status
from rest_framework.decorators import api_view
-from rest_framework.response import Response
from backend.core.api.public.decorators import require_scopes
from backend.core.api.public.types import APIRequest
+from backend.core.api.public.helpers.response import APIResponse
+
from backend.models import Invoice, QuotaLimit
@@ -16,13 +17,13 @@ def delete_invoice_endpoint(request: APIRequest):
try:
invoice = Invoice.objects.get(id=delete_items.get("invoice", ""))
except Invoice.DoesNotExist:
- return Response({"error": "Invoice Not Found"}, status=status.HTTP_404_NOT_FOUND)
+ return APIResponse(False, {"error": "Invoice Not Found"}, status=status.HTTP_404_NOT_FOUND)
if not invoice.has_access(request.user):
- return Response({"error": "You do not have permission to delete this invoice"}, status=status.HTTP_403_FORBIDDEN)
+ return APIResponse(False, {"error": "You do not have permission to delete this invoice"}, status=status.HTTP_403_FORBIDDEN)
QuotaLimit.delete_quota_usage("invoices-count", request.user, invoice.id, invoice.date_created)
invoice.delete()
- return Response({"message": "Invoice successfully deleted"}, status=status.HTTP_200_OK)
+ return APIResponse(True, {"message": "Invoice successfully deleted"}, status=status.HTTP_200_OK)
diff --git a/backend/core/api/public/endpoints/Invoices/download_pdf.py b/backend/core/api/public/endpoints/Invoices/download_pdf.py
index 1d6a885e1..d49c18224 100644
--- a/backend/core/api/public/endpoints/Invoices/download_pdf.py
+++ b/backend/core/api/public/endpoints/Invoices/download_pdf.py
@@ -13,6 +13,7 @@
from backend.core.api.public.types import APIRequest
from backend.finance.models import Invoice
from backend.core.service.invoices.single.create_pdf import generate_pdf
+from backend.core.api.public.helpers.response import APIResponse
@swagger_auto_schema(
@@ -68,8 +69,8 @@ def download(request: APIRequest, id: str) -> HttpResponse | Response:
else:
invoice = Invoice.objects.get(user=request.user, id=id)
except Invoice.DoesNotExist:
- return Response({"success": False, "message": "Invoice not found"}, status=status.HTTP_400_BAD_REQUEST)
+ return APIResponse(False, {"message": "Invoice not found"}, status=status.HTTP_400_BAD_REQUEST)
if response := generate_pdf(invoice, "attachment"):
return response
- return Response({"success": False, "message": "Error generating PDF"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
+ return APIResponse(False, {"message": "Error generating PDF"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
diff --git a/backend/core/api/public/endpoints/Invoices/edit.py b/backend/core/api/public/endpoints/Invoices/edit.py
index ada5aef10..145666bdb 100644
--- a/backend/core/api/public/endpoints/Invoices/edit.py
+++ b/backend/core/api/public/endpoints/Invoices/edit.py
@@ -3,10 +3,10 @@
from rest_framework import status
from rest_framework.decorators import api_view
-from rest_framework.response import Response
from backend.core.api.public.decorators import require_scopes
from backend.core.api.public.types import APIRequest
+from backend.core.api.public.helpers.response import APIResponse
from backend.finance.models import Invoice
@@ -15,20 +15,22 @@
def edit_invoice_endpoint(request: APIRequest):
invoice_id = request.data.get("invoice_id", "")
if not invoice_id:
- return Response({"error": "Invoice ID is required"}, status=status.HTTP_400_BAD_REQUEST)
+ return APIResponse(False, {"error": "Invoice ID is required"}, status=status.HTTP_400_BAD_REQUEST)
try:
invoice = Invoice.objects.get(id=invoice_id)
except Invoice.DoesNotExist:
- return Response({"error": "Invoice Not Found"}, status=status.HTTP_404_NOT_FOUND)
+ return APIResponse(False, {"error": "Invoice Not Found"}, status=status.HTTP_404_NOT_FOUND)
if request.user.logged_in_as_team and request.user.logged_in_as_team != invoice.organization:
- return Response(
+ return APIResponse(
+ False,
{"error": "You do not have permission to edit this invoice"},
status=status.HTTP_403_FORBIDDEN,
)
elif request.user != invoice.user:
- return Response(
+ return APIResponse(
+ False,
{"error": "You do not have permission to edit this invoice"},
status=status.HTTP_403_FORBIDDEN,
)
@@ -64,12 +66,12 @@ def edit_invoice_endpoint(request: APIRequest):
try:
new_value = datetime.strptime(new_value, "%Y-%m-%d").date() # type: ignore[assignment]
except ValueError:
- return Response({"error": "Invalid date format for date_due"}, status=status.HTTP_400_BAD_REQUEST)
+ return APIResponse(False, {"error": "Invalid date format for date_due"}, status=status.HTTP_400_BAD_REQUEST)
setattr(invoice, column_name, new_value)
invoice.save()
- return Response({"message": "Invoice successfully edited"}, status=status.HTTP_200_OK)
+ return APIResponse(True, {"message": "Invoice successfully edited"}, status=status.HTTP_200_OK)
@api_view(["POST"])
@@ -79,20 +81,20 @@ def change_status_endpoint(request, invoice_id: int, invoice_status: str):
try:
invoice = Invoice.objects.get(id=invoice_id)
except Invoice.DoesNotExist:
- return Response({"error": "Invoice Not Found"}, status=status.HTTP_404_NOT_FOUND)
+ return APIResponse(False, {"error": "Invoice Not Found"}, status=status.HTTP_404_NOT_FOUND)
if request.user.logged_in_as_team and request.user.logged_in_as_team != invoice.organization or request.user != invoice.user:
- return Response({"error": "You do not have permission to edit this invoice"}, status=status.HTTP_403_FORBIDDEN)
+ return APIResponse(False, {"error": "You do not have permission to edit this invoice"}, status=status.HTTP_403_FORBIDDEN)
if invoice_status not in ["paid", "draft", "pending"]:
- return Response({"error": "Invalid status. Please choose from: pending, paid, draft"}, status=status.HTTP_400_BAD_REQUEST)
+ return APIResponse(False, {"error": "Invalid status. Please choose from: pending, paid, draft"}, status=status.HTTP_400_BAD_REQUEST)
if invoice.status == invoice_status:
- return Response({"error": f"Invoice status is already {invoice_status}"}, status=status.HTTP_400_BAD_REQUEST)
+ return APIResponse(False, {"error": f"Invoice status is already {invoice_status}"}, status=status.HTTP_400_BAD_REQUEST)
invoice.set_status(invoice_status)
- return Response({"message": f"Invoice status been changed to {invoice_status}"}, status=status.HTTP_200_OK)
+ return APIResponse(True, {"message": f"Invoice status been changed to {invoice_status}"}, status=status.HTTP_200_OK)
@api_view(["POST"])
@@ -104,10 +106,10 @@ def edit_discount_endpoint(request, invoice_id: str):
try:
invoice: Invoice = Invoice.objects.get(id=invoice_id)
except Invoice.DoesNotExist:
- return Response({"error": "Invoice not found"}, status=status.HTTP_404_NOT_FOUND)
+ return APIResponse(False, {"error": "Invoice not found"}, status=status.HTTP_404_NOT_FOUND)
if not invoice.has_access(request.user):
- return Response({"error": "You don't have permission to make changes to this invoice."}, status=status.HTTP_403_FORBIDDEN)
+ return APIResponse(False, {"error": "You don't have permission to make changes to this invoice."}, status=status.HTTP_403_FORBIDDEN)
if discount_type == "percentage":
try:
@@ -115,7 +117,9 @@ def edit_discount_endpoint(request, invoice_id: str):
if percentage_amount < 0 or percentage_amount > 100:
raise ValueError
except ValueError:
- return Response({"error": "Please enter a valid percentage amount (between 0 and 100)"}, status=status.HTTP_400_BAD_REQUEST)
+ return APIResponse(
+ False, {"error": "Please enter a valid percentage amount (between 0 and 100)"}, status=status.HTTP_400_BAD_REQUEST
+ )
invoice.discount_percentage = percentage_amount
else:
try:
@@ -123,9 +127,9 @@ def edit_discount_endpoint(request, invoice_id: str):
if discount_amount < 0:
raise ValueError
except ValueError:
- return Response({"error": "Please enter a valid discount amount"}, status=status.HTTP_400_BAD_REQUEST)
+ return APIResponse(False, {"error": "Please enter a valid discount amount"}, status=status.HTTP_400_BAD_REQUEST)
invoice.discount_amount = discount_amount
invoice.save()
- return Response({"message": "Discount was applied successfully"}, status=status.HTTP_200_OK)
+ return APIResponse(True, {"message": "Discount was applied successfully"}, status=status.HTTP_200_OK)
diff --git a/backend/core/api/public/endpoints/Invoices/get.py b/backend/core/api/public/endpoints/Invoices/get.py
index d20caac88..044be292e 100644
--- a/backend/core/api/public/endpoints/Invoices/get.py
+++ b/backend/core/api/public/endpoints/Invoices/get.py
@@ -8,6 +8,7 @@
from backend.core.api.public.serializers.invoices import InvoiceSerializer
from backend.core.api.public.swagger_ui import TEAM_PARAMETER
from backend.core.api.public.types import APIRequest
+from backend.core.api.public.helpers.response import APIResponse
from backend.finance.models import Invoice
@@ -52,8 +53,8 @@ def get_invoices_endpoint(request: APIRequest, id: str) -> Response:
else:
invoices = Invoice.objects.filter(user=request.user, id=id)
except Invoice.DoesNotExist:
- return Response({"success": False, "message": "Invoice not found"}, status=status.HTTP_400_BAD_REQUEST)
+ return APIResponse(False, {"message": "Invoice not found"}, status=status.HTTP_400_BAD_REQUEST)
serializer = InvoiceSerializer(invoices, many=True)
- return Response({"success": True, "invoice": serializer.data}, status=status.HTTP_200_OK)
+ return APIResponse(True, {"invoice": serializer.data}, status=status.HTTP_200_OK)
diff --git a/backend/core/api/public/endpoints/clients/delete.py b/backend/core/api/public/endpoints/clients/delete.py
index 66e997ec4..398f77f1a 100644
--- a/backend/core/api/public/endpoints/clients/delete.py
+++ b/backend/core/api/public/endpoints/clients/delete.py
@@ -3,13 +3,13 @@
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from rest_framework.decorators import api_view
-from rest_framework.response import Response
from backend.core.api.public.decorators import require_scopes
from backend.core.api.public.swagger_ui import TEAM_PARAMETER
from backend.core.api.public.types import APIRequest
from backend.core.service.clients.delete import delete_client, DeleteClientServiceResponse
+from backend.core.api.public.helpers.response import APIResponse
@swagger_auto_schema(
@@ -64,5 +64,5 @@ def client_delete_endpoint(request: APIRequest, id: str):
response: DeleteClientServiceResponse = delete_client(request, id)
if response.failed:
- return Response({"success": False, "message": response.error}, status=403 if "do not have permission" in response.error else 404)
- return Response({"success": True, "client_id": id}, status=200)
+ return APIResponse(False, response.error, status=403 if "do not have permission" in response.error else 404)
+ return APIResponse(True, {"client_id": id}, status=200)
diff --git a/backend/core/api/public/endpoints/system_health.py b/backend/core/api/public/endpoints/system_health.py
index 43d4a136b..391ae4fcc 100644
--- a/backend/core/api/public/endpoints/system_health.py
+++ b/backend/core/api/public/endpoints/system_health.py
@@ -4,9 +4,9 @@
from django.core.cache import cache
from rest_framework.decorators import api_view, permission_classes
-from rest_framework.response import Response
from backend.core.api.public.permissions import IsSuperuser
+from backend.core.api.public.helpers.response import APIResponse
@swagger_auto_schema(
@@ -46,7 +46,7 @@
@permission_classes([IsSuperuser])
def system_health_endpoint(request):
if not request.user or not request.user.is_superuser:
- return Response({"success": False, "message": "User is not permitted to view internal information"}, status=403)
+ return APIResponse(False, "User is not permitted to view internal information", status=403)
problems = []
@@ -60,4 +60,4 @@ def system_health_endpoint(request):
except ConnectionError:
problems.append({"id": "redis", "message": "redis failed to connect"})
- return Response({"problems": problems, "healthy": not bool(problems)})
+ return APIResponse({"problems": problems, "healthy": not bool(problems)})
diff --git a/backend/core/api/public/endpoints/webhooks/webhook_task_queue_handler.py b/backend/core/api/public/endpoints/webhooks/webhook_task_queue_handler.py
index 3f8c4b44d..256f302ce 100644
--- a/backend/core/api/public/endpoints/webhooks/webhook_task_queue_handler.py
+++ b/backend/core/api/public/endpoints/webhooks/webhook_task_queue_handler.py
@@ -1,9 +1,9 @@
-from rest_framework.response import Response
import logging
from backend.core.api.public import APIAuthToken
from rest_framework.decorators import api_view
from backend.core.service.asyn_tasks.tasks import Task
+from backend.core.api.public.helpers.response import APIResponse
@api_view(["POST"])
@@ -11,10 +11,10 @@ def webhook_task_queue_handler_view_endpoint(request):
token: APIAuthToken | None = request.auth
if not token:
- return Response({"status": "error", "message": "No token found"}, status=500)
+ return APIResponse(False, {"status": "error", "message": "No token found"}, status=500)
if not token.administrator_service_type == token.AdministratorServiceTypes.AWS_WEBHOOK_CALLBACK:
- return Response({"status": "error", "message": "Invalid API key for this service"}, status=500)
+ return APIResponse(False, {"status": "error", "message": "Invalid API key for this service"}, status=500)
try:
data: dict = request.data
@@ -39,8 +39,8 @@ def webhook_task_queue_handler_view_endpoint(request):
# Handle the result (e.g., store it or log it)
print(f"Webhook executed: {func_name} with result: {result}")
- return Response({"status": "success", "result": result})
+ return APIResponse(True, {"status": "success", "result": result})
except Exception as e:
logging.error(f"Error executing webhook task: {str(e)}")
- return Response({"status": "error", "message": "An internal error has occurred."}, status=500)
+ return APIResponse(False, {"status": "error", "message": "An internal error has occurred."}, status=500)