From af669d2f37f979df9ec81363c39cb4f73532decd Mon Sep 17 00:00:00 2001 From: Nick Sweeting Date: Thu, 25 Apr 2024 05:49:37 -0700 Subject: [PATCH] rename api files for clarity --- archivebox/api/urls.py | 108 ++--------------- archivebox/api/v1_api.py | 109 ++++++++++++++++++ archivebox/api/{routes_auth.py => v1_auth.py} | 0 archivebox/api/{routes_cli.py => v1_cli.py} | 2 - archivebox/api/{routes_core.py => v1_core.py} | 0 5 files changed, 116 insertions(+), 103 deletions(-) create mode 100644 archivebox/api/v1_api.py rename archivebox/api/{routes_auth.py => v1_auth.py} (100%) rename archivebox/api/{routes_cli.py => v1_cli.py} (98%) rename archivebox/api/{routes_core.py => v1_core.py} (100%) diff --git a/archivebox/api/urls.py b/archivebox/api/urls.py index 5d3877e9..81f8cb43 100644 --- a/archivebox/api/urls.py +++ b/archivebox/api/urls.py @@ -1,111 +1,17 @@ __package__ = 'archivebox.api' -# import orjson - -from io import StringIO -from traceback import format_exception -from contextlib import redirect_stdout, redirect_stderr - from django.urls import path -from django.http import HttpRequest, HttpResponse from django.views.generic.base import RedirectView -from django.core.exceptions import ObjectDoesNotExist, EmptyResultSet, PermissionDenied - -from ninja import NinjaAPI, Swagger - -# TODO: explore adding https://eadwincode.github.io/django-ninja-extra/ - -from api.auth import API_AUTH_METHODS -from ..config import VERSION, COMMIT_HASH - -# from ninja.renderers import BaseRenderer - -# class ORJSONRenderer(BaseRenderer): -# media_type = "application/json" - -# def render(self, request, data, *, response_status): -# return { -# "success": True, -# "errors": [], -# "result": data, -# "stdout": ansi_to_html(stdout.getvalue().strip()), -# "stderr": ansi_to_html(stderr.getvalue().strip()), -# } -# return orjson.dumps(data) - - -class NinjaAPIWithIOCapture(NinjaAPI): - def create_temporal_response(self, request: HttpRequest) -> HttpResponse: - stdout, stderr = StringIO(), StringIO() - - with redirect_stderr(stderr): - with redirect_stdout(stdout): - request.stdout = stdout - request.stderr = stderr - - response = super().create_temporal_response(request) - - print('RESPONDING NOW', response) - - return response - -html_description=f''' -

Welcome to your ArchiveBox server's REST API [v1 ALPHA] homepage!

-
-WARNING: This API is still in an early development stage and may change! -
- -Served by ArchiveBox v{VERSION} ({COMMIT_HASH[:8]}), API powered by django-ninja. -''' - -api = NinjaAPIWithIOCapture( - title='ArchiveBox API', - description=html_description, - version='1.0.0', - csrf=False, - auth=API_AUTH_METHODS, - urls_namespace="api", - docs=Swagger(settings={"persistAuthorization": True}), - # docs_decorator=login_required, - # renderer=ORJSONRenderer(), -) -api.add_router('/auth/', 'api.routes_auth.router') -api.add_router('/core/', 'api.routes_core.router') -api.add_router('/cli/', 'api.routes_cli.router') - - -@api.exception_handler(Exception) -def generic_exception_handler(request, err): - status = 503 - if isinstance(err, (ObjectDoesNotExist, EmptyResultSet, PermissionDenied)): - status = 404 - - print(''.join(format_exception(err))) - - return api.create_response( - request, - { - "succeeded": False, - "errors": [ - ''.join(format_exception(err)), - # or send simpler exception-only summary without full traceback: - # f'{err.__class__.__name__}: {err}', - # *([str(err.__context__)] if getattr(err, '__context__', None) else []), - ], - }, - status=status, - ) +from .v1_api import urls as v1_api_urls urlpatterns = [ - path("v1/", api.urls), + path("", RedirectView.as_view(url='/api/v1')), + path("v1/", v1_api_urls), path("v1", RedirectView.as_view(url='/api/v1/docs')), - path("", RedirectView.as_view(url='/api/v1/docs')), + + # ... v2 can be added here ... + # path("v2/", v2_api_urls), + # path("v2", RedirectView.as_view(url='/api/v2/docs')), ] diff --git a/archivebox/api/v1_api.py b/archivebox/api/v1_api.py new file mode 100644 index 00000000..6b2c8c63 --- /dev/null +++ b/archivebox/api/v1_api.py @@ -0,0 +1,109 @@ +__package__ = 'archivebox.api' + + +from io import StringIO +from traceback import format_exception +from contextlib import redirect_stdout, redirect_stderr + +from django.http import HttpRequest, HttpResponse +from django.core.exceptions import ObjectDoesNotExist, EmptyResultSet, PermissionDenied + +from ninja import NinjaAPI, Swagger + +# TODO: explore adding https://eadwincode.github.io/django-ninja-extra/ + +from api.auth import API_AUTH_METHODS +from ..config import VERSION, COMMIT_HASH + + +html_description=f''' +

Welcome to your ArchiveBox server's REST API [v1 ALPHA] homepage!

+
+WARNING: This API is still in an early development stage and may change! +
+ +Served by ArchiveBox v{VERSION} ({COMMIT_HASH[:8]}), API powered by django-ninja. +''' + + +def register_urls(api: NinjaAPI) -> NinjaAPI: + api.add_router('/auth/', 'api.v1_auth.router') + api.add_router('/core/', 'api.v1_core.router') + api.add_router('/cli/', 'api.v1_cli.router') + return api + + +class NinjaAPIWithIOCapture(NinjaAPI): + def create_temporal_response(self, request: HttpRequest) -> HttpResponse: + stdout, stderr = StringIO(), StringIO() + + with redirect_stderr(stderr): + with redirect_stdout(stdout): + request.stdout = stdout + request.stderr = stderr + + response = super().create_temporal_response(request) + + print('RESPONDING NOW', response) + + return response + + +api = NinjaAPIWithIOCapture( + title='ArchiveBox API', + description=html_description, + version='1.0.0', + csrf=False, + auth=API_AUTH_METHODS, + urls_namespace="api", + docs=Swagger(settings={"persistAuthorization": True}), + # docs_decorator=login_required, + # renderer=ORJSONRenderer(), +) +api = register_urls(api) +urls = api.urls + + +@api.exception_handler(Exception) +def generic_exception_handler(request, err): + status = 503 + if isinstance(err, (ObjectDoesNotExist, EmptyResultSet, PermissionDenied)): + status = 404 + + print(''.join(format_exception(err))) + + return api.create_response( + request, + { + "succeeded": False, + "message": f'{err.__class__.__name__}: {err}', + "errors": [ + ''.join(format_exception(err)), + # or send simpler parent-only traceback: + # *([str(err.__context__)] if getattr(err, '__context__', None) else []), + ], + }, + status=status, + ) + + + +# import orjson +# from ninja.renderers import BaseRenderer +# class ORJSONRenderer(BaseRenderer): +# media_type = "application/json" +# def render(self, request, data, *, response_status): +# return { +# "success": True, +# "errors": [], +# "result": data, +# "stdout": ansi_to_html(stdout.getvalue().strip()), +# "stderr": ansi_to_html(stderr.getvalue().strip()), +# } +# return orjson.dumps(data) diff --git a/archivebox/api/routes_auth.py b/archivebox/api/v1_auth.py similarity index 100% rename from archivebox/api/routes_auth.py rename to archivebox/api/v1_auth.py diff --git a/archivebox/api/routes_cli.py b/archivebox/api/v1_cli.py similarity index 98% rename from archivebox/api/routes_cli.py rename to archivebox/api/v1_cli.py index 4bef5088..6e737464 100644 --- a/archivebox/api/routes_cli.py +++ b/archivebox/api/v1_cli.py @@ -3,8 +3,6 @@ __package__ = 'archivebox.api' from typing import List, Dict, Any, Optional from enum import Enum -# from pydantic import BaseModel -from archivebox.api.routes_core import paginate from ninja import Router, Schema from ..main import ( diff --git a/archivebox/api/routes_core.py b/archivebox/api/v1_core.py similarity index 100% rename from archivebox/api/routes_core.py rename to archivebox/api/v1_core.py