Skip to content

Commit

Permalink
Merge pull request #529 from guninwasan/bug/issue-517_update-api-resp…
Browse files Browse the repository at this point in the history
…onse-calls
  • Loading branch information
TreyWW authored Nov 11, 2024
2 parents 1df1324 + 4c2f4bd commit a6f215c
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 41 deletions.
10 changes: 5 additions & 5 deletions backend/core/api/public/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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()

Expand Down
9 changes: 5 additions & 4 deletions backend/core/api/public/endpoints/Invoices/delete.py
Original file line number Diff line number Diff line change
@@ -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


Expand All @@ -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)
5 changes: 3 additions & 2 deletions backend/core/api/public/endpoints/Invoices/download_pdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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)
38 changes: 21 additions & 17 deletions backend/core/api/public/endpoints/Invoices/edit.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand All @@ -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,
)
Expand Down Expand Up @@ -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"])
Expand All @@ -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 <strong>{invoice_status}</strong>"}, status=status.HTTP_200_OK)
return APIResponse(True, {"message": f"Invoice status been changed to <strong>{invoice_status}</strong>"}, status=status.HTTP_200_OK)


@api_view(["POST"])
Expand All @@ -104,28 +106,30 @@ 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:
percentage_amount = int(percentage_amount_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:
discount_amount = int(discount_amount_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)
5 changes: 3 additions & 2 deletions backend/core/api/public/endpoints/Invoices/get.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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)
6 changes: 3 additions & 3 deletions backend/core/api/public/endpoints/clients/delete.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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)
6 changes: 3 additions & 3 deletions backend/core/api/public/endpoints/system_health.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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 = []

Expand All @@ -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)})
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
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"])
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
Expand All @@ -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)

0 comments on commit a6f215c

Please sign in to comment.