Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
81 changes: 81 additions & 0 deletions temba/api/v2/projects/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import uuid
from functools import wraps
from unittest.mock import patch

from rest_framework.authentication import BasicAuthentication

from django.test import SimpleTestCase, override_settings
from django.urls import resolve, reverse

from temba.api.v2.projects.views import GetProjectView
from temba.tests import TembaTest

GET_PROJECT_VIEW_PATH = "temba.api.v2.projects.views.GetProjectView"


def skip_auth_and_permissions(view_path: str):
"""
Decorator to disable authentication and permission checks for a specific endpoint class.
Use when testing view logic (status codes and payload) without auth side-effects.
"""

def decorator(func):
@patch(f"{view_path}.authentication_classes", [])
@patch(f"{view_path}.permission_classes", [])
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)

return wrapper

return decorator


class GetProjectViewTest(TembaTest):
def setUp(self):
super().setUp()

# Ensure org has a project UUID for lookups
self.org.proj_uuid = uuid.uuid4()
self.org.save(update_fields=("proj_uuid",))

self.url = reverse("api.v2.projects")

@patch(f"{GET_PROJECT_VIEW_PATH}.authentication_classes", [BasicAuthentication])
def test_requires_authentication(self):
# Without authentication headers, endpoint should deny access
resp = self.client.get(f"{self.url}?project_uuid={self.org.proj_uuid}")
self.assertEqual(resp.status_code, 401)

@skip_auth_and_permissions(GET_PROJECT_VIEW_PATH)
def test_missing_project_uuid_returns_400(self):
resp = self.client.get(self.url)
self.assertEqual(resp.status_code, 400)
self.assertEqual(resp.json(), {"error": "project_uuid is required"})

@skip_auth_and_permissions(GET_PROJECT_VIEW_PATH)
def test_nonexistent_project_returns_404(self):
random_proj = uuid.uuid4()
resp = self.client.get(f"{self.url}?project_uuid={random_proj}")
self.assertEqual(resp.status_code, 404)
self.assertEqual(resp.json(), {"error": "Project not found"})

@skip_auth_and_permissions(GET_PROJECT_VIEW_PATH)
def test_success_returns_project_data(self):
resp = self.client.get(f"{self.url}?project_uuid={self.org.proj_uuid}")
self.assertEqual(resp.status_code, 200)
data = resp.json()

self.assertEqual(data["project_uuid"], str(self.org.proj_uuid))
self.assertEqual(data["name"], self.org.name)
self.assertEqual(data["is_active"], self.org.is_active)
self.assertEqual(data["brain_on"], self.org.brain_on)


@override_settings(ROOT_URLCONF="temba.api.v2.projects.urls")
class ProjectsUrlsTest(SimpleTestCase):
def test_projects_url_resolves_to_get_project_view(self):
url = reverse("projects")
self.assertEqual(url, "/projects")
match = resolve(url)
self.assertEqual(getattr(match.func, "view_class", None), GetProjectView)
7 changes: 7 additions & 0 deletions temba/api/v2/projects/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from django.urls import path

from .views import GetProjectView

urlpatterns = [
path("projects", GetProjectView.as_view(), name="projects"),
]
34 changes: 34 additions & 0 deletions temba/api/v2/projects/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from rest_framework.permissions import IsAuthenticated
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView
from weni.internal.authenticators import InternalOIDCAuthentication

from temba.api.v2.internals.views import APIViewMixin
from temba.api.v2.permissions import IsUserInOrg
from temba.orgs.models import Org


class GetProjectView(APIViewMixin, APIView):
authentication_classes = [InternalOIDCAuthentication]
permission_classes = [IsAuthenticated, IsUserInOrg]

def get(self, request: Request):
params = request.query_params
project_uuid = params.get("project_uuid")

if project_uuid is None:
return Response(status=400, data={"error": "project_uuid is required"})

try:
org = Org.objects.get(proj_uuid=project_uuid)
except Org.DoesNotExist:
return Response(status=404, data={"error": "Project not found"})

project_data = {
"project_uuid": str(org.proj_uuid),
"name": org.name,
"is_active": org.is_active,
"brain_on": org.brain_on,
}
return Response(project_data)
2 changes: 2 additions & 0 deletions temba/api/v2/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.urls import include, path

from temba.api.v2.elasticsearch.views import ContactsElasticSearchEndpoint
from temba.api.v2.projects.views import GetProjectView

from .flows.urls import urlpatterns as flows_urlpatterns
from .internals.urls import urlpatterns as internals_urlpatterns
Expand Down Expand Up @@ -110,6 +111,7 @@
url(r"^intelligences$", IntelligencesEndpoint.as_view(), name="api.v2.intelligences"),
url(r"^brain_info$", BrainInfoEndpoint.as_view(), name="api.v2.brain_info"),
url(r"^whatsapp_flows$", WhatsappFlowsEndpoint.as_view(), name="api.v2.whatsapp_flows"),
url(r"^projects$", GetProjectView.as_view(), name="api.v2.projects"),
]

urlpatterns = format_suffix_patterns(urlpatterns, allowed=["json", "api"])
Expand Down