From 34d2084261ce2bd94a0f479e1a4f87724d754cf5 Mon Sep 17 00:00:00 2001 From: hyukychang Date: Thu, 2 May 2024 01:16:09 +0900 Subject: [PATCH] feat: add django ninja base controller --- ara/common/__init__.py | 0 ara/common/exceptions/ara_exception.py | 20 +++++++++++++ ara/controller/api.py | 36 ++++++++++++++++++++++++ ara/controller/authentication.py | 30 ++++++++++++++++++-- ara/controller/ping/__init__.py | 1 + ara/controller/ping/ping_router.py | 39 ++++++++++++++++++++++++++ ara/controller/urls.py | 10 +++++++ ara/urls.py | 5 ++++ 8 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 ara/common/__init__.py create mode 100644 ara/common/exceptions/ara_exception.py create mode 100644 ara/controller/ping/__init__.py create mode 100644 ara/controller/ping/ping_router.py diff --git a/ara/common/__init__.py b/ara/common/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ara/common/exceptions/ara_exception.py b/ara/common/exceptions/ara_exception.py new file mode 100644 index 00000000..090f87fd --- /dev/null +++ b/ara/common/exceptions/ara_exception.py @@ -0,0 +1,20 @@ +from ara.controller.constants import HttpStatusCode + + +class AraException(Exception): + error_code: int | None + error_reason: str | None + + +class TooManyRequestException(AraException): + """too many requests exception""" + + error_code = HttpStatusCode.TOO_MANY_REQUESTS + error_reason = "Too many requests" + + +class InvalidRequestException(AraException): + """invalid request exception""" + + error_code = HttpStatusCode.BAD_REQUEST + error_reason = "Invalid request" diff --git a/ara/controller/api.py b/ara/controller/api.py index e69de29b..cd3e155e 100644 --- a/ara/controller/api.py +++ b/ara/controller/api.py @@ -0,0 +1,36 @@ +from ninja import NinjaAPI +from ninja.errors import ValidationError +from ratelimit.exception import RateLimitException + +from ara.common.exceptions.ara_exception import ( + InvalidRequestException, + TooManyRequestException, +) +from ara.controller.constants import HttpStatusCode +from ara.controller.ping import router as ping_router +from ara.controller.response import AraErrorResponseBody +from ara.settings import env + +docs_url = None if env("DJANGO_ENV") == "production" else "/docs" +api = NinjaAPI(docs_url=docs_url) + + +@api.exception_handler(RateLimitException) +def too_many_requests(request, exception): + return api.create_response( + request, + AraErrorResponseBody(TooManyRequestException()), + status=HttpStatusCode.TOO_MANY_REQUESTS, + ) + + +@api.exception_handler(ValidationError) +def invalid_request(request, exception): + return api.create_response( + request, + AraErrorResponseBody(InvalidRequestException()), + status=HttpStatusCode.BAD_REQUEST, + ) + + +api.add_router("/ping", ping_router) diff --git a/ara/controller/authentication.py b/ara/controller/authentication.py index 017a67e5..6791eba7 100644 --- a/ara/controller/authentication.py +++ b/ara/controller/authentication.py @@ -1,3 +1,27 @@ -class AuthLoggedInUser: - # TODO - pass +from typing import Any + +from django.http import HttpRequest +from ninja.security import HttpBearer +from rest_framework.authentication import SessionAuthentication + + +class AuthLoggedInUser(HttpBearer): + def __call__(self, request: HttpRequest) -> Any | None: + headers = request.headers + auth_value = headers.get(self.header) + if not auth_value: + return None + parts = auth_value.split(" ") + + if parts[0].lower() != self.openapi_scheme: + return None + return self.authenticate(request) + + def authenticate(self, request: HttpRequest, token: str) -> bool: + result = SessionAuthentication().authenticate(request) + if result is None: + return False + (user, _) = result + request.user = user + + return bool(user and user.is_authenticated) diff --git a/ara/controller/ping/__init__.py b/ara/controller/ping/__init__.py new file mode 100644 index 00000000..d3b9e616 --- /dev/null +++ b/ara/controller/ping/__init__.py @@ -0,0 +1 @@ +from .ping_router import router diff --git a/ara/controller/ping/ping_router.py b/ara/controller/ping/ping_router.py new file mode 100644 index 00000000..1477633d --- /dev/null +++ b/ara/controller/ping/ping_router.py @@ -0,0 +1,39 @@ +from django.http import HttpRequest +from ninja import Router + +from ara.controller.authentication import AuthLoggedInUser +from ara.controller.constants import HttpStatusCode +from ara.controller.response import AraResponse + +router = Router() + + +@router.get( + "/", + response={ + HttpStatusCode.OK: str, + HttpStatusCode.INTERNAL_SERVER_ERROR: str, + }, +) +def ping(request: HttpRequest): + return AraResponse( + status_code=HttpStatusCode.OK, + data="pong", + ) + + +auth_router = Router(auth=AuthLoggedInUser()) + + +@auth_router.get( + "/auth", + response={ + HttpStatusCode.OK: str, + HttpStatusCode.BAD_REQUEST: str, + }, +) +def auth_ping(request: HttpRequest): + return AraResponse( + status_code=HttpStatusCode.OK, + data="pong", + ) diff --git a/ara/controller/urls.py b/ara/controller/urls.py index e69de29b..9f5253d7 100644 --- a/ara/controller/urls.py +++ b/ara/controller/urls.py @@ -0,0 +1,10 @@ +from django.urls import path + +from ara.controller.api import api + +urlpatterns = [ + path( + "", + api.urls, + ) +] diff --git a/ara/urls.py b/ara/urls.py index 2341a60e..b8c99a50 100644 --- a/ara/urls.py +++ b/ara/urls.py @@ -13,6 +13,7 @@ 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ + from django.conf import settings from django.contrib import admin from django.urls import include, path @@ -22,6 +23,8 @@ SpectacularSwaggerView, ) +from .controller import urls as v2_urls + urlpatterns = [ path("api/admin/", admin.site.urls), path("", include(("apps.core.urls", "core"))), @@ -41,6 +44,8 @@ ), ] +urlpatterns.extend(v2_urls.urlpatterns) + if settings.DEBUG: urlpatterns += [ path("api/__debug__/", include("debug_toolbar.urls")),