mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-18 05:51:26 +02:00
Merge pull request #2072 from modoboa/feature/spectacular
Added drf-spectacular.
This commit is contained in:
+16
-10
@@ -4,6 +4,7 @@ from django import http
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from drf_spectacular.utils import extend_schema, extend_schema_view
|
||||
from rest_framework import filters, status, viewsets
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.exceptions import ParseError
|
||||
@@ -16,17 +17,22 @@ from modoboa.core import sms_backends
|
||||
from . import lib, models, serializers
|
||||
|
||||
|
||||
@extend_schema_view(
|
||||
retrieve=extend_schema(
|
||||
description="Retrieve a particular domain",
|
||||
summary="Retrieve a particular domain"
|
||||
),
|
||||
list=extend_schema(
|
||||
description="Retrieve a list of domains",
|
||||
summary="Retrieve a list of domains"
|
||||
),
|
||||
create=extend_schema(
|
||||
description="Create a new domain",
|
||||
summary="Create a new domain"
|
||||
)
|
||||
)
|
||||
class DomainViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
retrieve:
|
||||
Return the given domain.
|
||||
|
||||
list:
|
||||
Return a list of all existing domains.
|
||||
|
||||
create:
|
||||
Create a new domain instance.
|
||||
"""
|
||||
"""Domain viewset."""
|
||||
|
||||
permission_classes = [IsAuthenticated, DjangoModelPermissions, ]
|
||||
serializer_class = serializers.DomainSerializer
|
||||
|
||||
@@ -7,6 +7,8 @@ from django.shortcuts import get_object_or_404
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import extend_schema_field
|
||||
from rest_framework import serializers
|
||||
|
||||
from modoboa.admin import models as admin_models
|
||||
@@ -186,6 +188,7 @@ class AccountSerializer(serializers.ModelSerializer):
|
||||
if not user.is_superuser:
|
||||
del self.fields["master_user"]
|
||||
|
||||
@extend_schema_field(OpenApiTypes.STR)
|
||||
def get_role(self, account):
|
||||
"""Return role."""
|
||||
return account.role
|
||||
|
||||
@@ -64,6 +64,7 @@ INSTALLED_APPS = (
|
||||
'ckeditor_uploader',
|
||||
'rest_framework',
|
||||
'rest_framework.authtoken',
|
||||
'drf_spectacular',
|
||||
'phonenumber_field',
|
||||
'django_otp',
|
||||
'django_otp.plugins.otp_totp',
|
||||
@@ -190,6 +191,14 @@ REST_FRAMEWORK = {
|
||||
'rest_framework.authentication.TokenAuthentication',
|
||||
'rest_framework.authentication.SessionAuthentication',
|
||||
),
|
||||
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
|
||||
}
|
||||
|
||||
SPECTACULAR_SETTINGS = {
|
||||
'SCHEMA_PATH_PREFIX': r'/api/v1',
|
||||
'TITLE': 'Modoboa API',
|
||||
'VERSION': '1.0.0',
|
||||
'SERVE_PERMISSIONS': ['rest_framework.permissions.IsAuthenticated'],
|
||||
}
|
||||
|
||||
# Modoboa settings
|
||||
|
||||
@@ -4,16 +4,13 @@ from modoboa.lib.tests import ModoTestCase
|
||||
|
||||
|
||||
class OpenAPITestCase(ModoTestCase):
|
||||
openapi_schema_url = reverse_lazy('openapi_schema')
|
||||
openapi_schema_url = reverse_lazy('schema')
|
||||
|
||||
def test_unauthorized(self):
|
||||
self.client.logout()
|
||||
|
||||
response = self.client.get("/docs/api/")
|
||||
self.assertRedirects(response, "/accounts/login/?next=/docs/api/")
|
||||
|
||||
response = self.client.get(self.openapi_schema_url)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
self.assertEqual(response.status_code, 401)
|
||||
|
||||
def test_get_schema(self):
|
||||
self.assertEqual(self.openapi_schema_url, "/docs/openapi.json")
|
||||
@@ -24,7 +21,3 @@ class OpenAPITestCase(ModoTestCase):
|
||||
'title': "Modoboa API",
|
||||
'version': "1.0.0",
|
||||
})
|
||||
|
||||
def test_get_swagger_ui(self):
|
||||
response = self.client.get("/docs/api/")
|
||||
self.assertContains(response, "Modoboa API")
|
||||
|
||||
@@ -7,6 +7,7 @@ from django_otp.plugins.otp_static.models import StaticDevice, StaticToken
|
||||
from django_otp.plugins.otp_totp.models import TOTPDevice
|
||||
from rest_framework import permissions, response, viewsets
|
||||
from rest_framework.decorators import action
|
||||
from drf_spectacular.utils import extend_schema
|
||||
|
||||
from . import serializers
|
||||
|
||||
@@ -28,6 +29,9 @@ class AccountViewSet(viewsets.ViewSet):
|
||||
)
|
||||
return response.Response()
|
||||
|
||||
@extend_schema(
|
||||
request=serializers.CheckTFASetupSerializer
|
||||
)
|
||||
@action(methods=["post"], detail=False, url_path="tfa/setup/check")
|
||||
def tfa_setup_check(self, request):
|
||||
"""Check TFA setup."""
|
||||
@@ -57,6 +61,7 @@ class AccountViewSet(viewsets.ViewSet):
|
||||
request.user.save()
|
||||
return response.Response()
|
||||
|
||||
@extend_schema(tags=['account'])
|
||||
@action(methods=["post"], detail=False, url_path="tfa/reset_codes")
|
||||
def tfa_reset_codes(self, request, *args, **kwargs):
|
||||
"""Reset recovery codes."""
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
{% load static %}
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Modoboa API</title>
|
||||
<link rel="stylesheet" href="{% static 'swagger-ui/dist/swagger-ui.css' %}">
|
||||
<style>
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
overflow: -moz-scrollbars-vertical;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.swagger-ui .highlight-code .download-contents {
|
||||
right: 20px;
|
||||
height: auto;
|
||||
width: auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
<script src="{% static 'swagger-ui/dist/swagger-ui-bundle.js' %}"></script>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
const ui = SwaggerUIBundle({
|
||||
url: "{% url 'openapi_schema' %}",
|
||||
dom_id: '#swagger-ui',
|
||||
deepLinking: true,
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis
|
||||
],
|
||||
layout: "BaseLayout",
|
||||
requestInterceptor: (request) => {
|
||||
request.headers['X-CSRFToken'] = "{{ csrf_token }}"
|
||||
return request;
|
||||
}
|
||||
});
|
||||
|
||||
window.ui = ui;
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+9
-13
@@ -6,11 +6,11 @@ from django.conf import settings
|
||||
from django.contrib.auth import views as auth_views
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.urls import include, path
|
||||
from django.views.generic import TemplateView
|
||||
from django.views.i18n import JavaScriptCatalog
|
||||
|
||||
from rest_framework.renderers import JSONOpenAPIRenderer
|
||||
from rest_framework.schemas import get_schema_view
|
||||
from drf_spectacular.views import (
|
||||
SpectacularJSONAPIView, SpectacularSwaggerView, SpectacularRedocView
|
||||
)
|
||||
|
||||
from modoboa.admin.views import user as user_views
|
||||
from modoboa.core import signals as core_signals, views as core_views
|
||||
@@ -59,17 +59,13 @@ if extra_routes:
|
||||
urlpatterns += extra_routes
|
||||
|
||||
# API urls
|
||||
schema_view = get_schema_view(
|
||||
title="Modoboa API",
|
||||
version="1.0.0",
|
||||
public=False,
|
||||
renderer_classes=[JSONOpenAPIRenderer],
|
||||
)
|
||||
urlpatterns += [
|
||||
path('docs/openapi.json', schema_view, name="openapi_schema"),
|
||||
path('docs/api/', login_required(
|
||||
TemplateView.as_view(template_name="swagger-ui.html")),
|
||||
name="docs-index"),
|
||||
path('docs/openapi.json', SpectacularJSONAPIView.as_view(), name='schema'),
|
||||
path('docs/api/',
|
||||
SpectacularSwaggerView.as_view(url_name='schema'),
|
||||
name='docs-index'),
|
||||
path('docs/api/redoc/', SpectacularRedocView.as_view(url_name='schema'),
|
||||
name='redoc'),
|
||||
path('api/v1/', include("modoboa.urls_api")),
|
||||
]
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ django-otp==1.0.2
|
||||
dj-database-url
|
||||
|
||||
djangorestframework>=3.11,<3.12
|
||||
drf-spectacular
|
||||
uritemplate
|
||||
|
||||
passlib~=1.7.4
|
||||
|
||||
@@ -63,6 +63,7 @@ INSTALLED_APPS = (
|
||||
'ckeditor_uploader',
|
||||
'rest_framework',
|
||||
'rest_framework.authtoken',
|
||||
'drf_spectacular',
|
||||
'django_otp',
|
||||
'django_otp.plugins.otp_totp',
|
||||
'django_otp.plugins.otp_static',
|
||||
@@ -184,6 +185,14 @@ REST_FRAMEWORK = {
|
||||
'rest_framework.authentication.TokenAuthentication',
|
||||
'rest_framework.authentication.SessionAuthentication',
|
||||
),
|
||||
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
|
||||
}
|
||||
|
||||
SPECTACULAR_SETTINGS = {
|
||||
'SCHEMA_PATH_PREFIX': r'/api/v1',
|
||||
'TITLE': 'Modoboa API',
|
||||
'VERSION': '1.0.0',
|
||||
'SERVE_PERMISSIONS': ['rest_framework.permissions.IsAuthenticated'],
|
||||
}
|
||||
|
||||
# Modoboa settings
|
||||
|
||||
Reference in New Issue
Block a user