Browse Source

Port wizard app to ninja

David Burke 9 months ago
parent
commit
4fc43e23cd
4 changed files with 66 additions and 10 deletions
  1. 37 0
      apps/organizations_ext/schemas.py
  2. 7 2
      apps/projects/schema.py
  3. 8 8
      apps/wizard/api.py
  4. 14 0
      glitchtip/api/authentication.py

+ 37 - 0
apps/organizations_ext/schemas.py

@@ -0,0 +1,37 @@
+from datetime import datetime
+from typing import Optional
+
+from ninja import Field, ModelSchema
+
+from glitchtip.schema import CamelSchema
+
+from .models import Organization
+
+
+class OrganizationSchema(CamelSchema, ModelSchema):
+    date_created: datetime = Field(validation_alias="created")
+    status: dict[str, str]
+    avatar: dict[str, Optional[str]]
+    is_early_adopter: bool = False
+    require2FA: bool = False
+
+    class Meta:
+        model = Organization
+        fields = (
+            "id",
+            "name",
+            "slug",
+            "is_accepting_events",
+        )
+
+    @staticmethod
+    def resolve_status(obj):
+        return {"id": "active", "name": "active"}
+
+    @staticmethod
+    def resolve_avatar(obj):
+        return {"avatarType": "", "avatarUuid": None}
+
+    @staticmethod
+    def resolve_require2FA(obj):
+        return False

+ 7 - 2
apps/projects/schema.py

@@ -6,6 +6,7 @@ from ninja import Field, ModelSchema
 
 from glitchtip.schema import CamelSchema
 
+from apps.organizations_ext.schemas import OrganizationSchema
 from .models import Project, ProjectKey
 
 
@@ -67,5 +68,9 @@ class ProjectKeySchema(CamelSchema, ModelSchema):
         }
 
 
-class ProjectWithKeysSchema(ProjectSchema):
-    keys: list[ProjectKeySchema]
+class ProjectOrganizationSchema(ProjectSchema):
+    organization: OrganizationSchema
+
+
+class ProjectWithKeysSchema(ProjectOrganizationSchema):
+    keys: list[ProjectKeySchema] = Field(validation_alias="projectkey_set")

+ 8 - 8
apps/wizard/api.py

@@ -34,7 +34,7 @@ router = Router()
 
 
 @router.get("wizard/", response=SetupWizardSchema, auth=None)
-def setup_wizard(request: AuthHttpRequest):
+def setup_wizard(request):
     wizard_hash = get_random_string(
         64, allowed_chars=string.ascii_lowercase + string.digits
     )
@@ -44,7 +44,7 @@ def setup_wizard(request: AuthHttpRequest):
 
 
 @router.get("wizard/{wizard_hash}/")
-async def setup_wizard_hash(request: AuthHttpRequest, wizard_hash: str, auth=None):
+async def setup_wizard_hash(request, wizard_hash: str, auth=None):
     key = SETUP_WIZARD_CACHE_KEY + wizard_hash
     wizard_data = cache.get(key)
 
@@ -52,11 +52,12 @@ async def setup_wizard_hash(request: AuthHttpRequest, wizard_hash: str, auth=Non
         raise Http404
     elif wizard_data == SETUP_WIZARD_CACHE_EMPTY:
         raise HttpError(400)
+
     return wizard_data
 
 
 @router.delete("wizard/{wizard_hash}/")
-def setup_wizard_delete(request: AuthHttpRequest, wizard_hash: str, auth=None):
+def setup_wizard_delete(request, wizard_hash: str, auth=None):
     cache.delete(SETUP_WIZARD_CACHE_KEY + wizard_hash)
 
 
@@ -71,11 +72,10 @@ async def setup_wizard_set_token(request: AuthHttpRequest, payload: SetupWizardS
     user_id = request.auth.user_id
     projects = [
         project
-        async for project in Project.objects.filter(
-            organization__users=user_id
-        ).annotate(
-            is_member=Count("team__members", filter=Q(team__members__id=user_id))
-        )[:50]
+        async for project in Project.objects.filter(organization__users=user_id)
+        .annotate(is_member=Count("team__members", filter=Q(team__members__id=user_id)))
+        .select_related("organization")
+        .prefetch_related("projectkey_set")[:50]
     ]
 
     scope = getattr(APIToken.scopes, "project:releases")

+ 14 - 0
glitchtip/api/authentication.py

@@ -4,6 +4,7 @@ from typing import Any, Literal, Optional
 from asgiref.sync import sync_to_async
 from django.conf import settings
 from django.contrib.auth import SESSION_KEY
+from django.contrib.auth.models import AnonymousUser
 from django.http import HttpRequest
 from ninja.security import HttpBearer
 from ninja.security import SessionAuth as BaseSessionAuth
@@ -48,6 +49,19 @@ class TokenAuth(HttpBearer):
     Store the token object under data for checking scopes permissions.
     """
 
+    def __call__(self, request: HttpRequest) -> Optional[Any]:
+        # Workaround https://github.com/vitalik/django-ninja/issues/989
+        if request.resolver_match and request.resolver_match.url_name in [
+            "setup_wizard_hash"
+        ]:
+            request.auth = None  # type: ignore
+            return self.get_anon()
+
+        return super().__call__(request)
+
+    async def get_anon(self):
+        return AnonymousUser()
+
     async def authenticate(self, request: HttpRequest, key: str) -> Optional[Auth]:
         try:
             token = await APIToken.objects.aget(token=key, user__is_active=True)