Merge pull request #2072 from modoboa/feature/spectacular

Added drf-spectacular.
This commit is contained in:
Antoine Nguyen
2020-11-10 16:52:01 +01:00
committed by GitHub
9 changed files with 54 additions and 89 deletions
+16 -10
View File
@@ -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
+3
View File
@@ -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
+2 -9
View File
@@ -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")
+5
View File
@@ -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."""
-57
View File
@@ -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
View File
@@ -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")),
]
+1
View File
@@ -9,6 +9,7 @@ django-otp==1.0.2
dj-database-url
djangorestframework>=3.11,<3.12
drf-spectacular
uritemplate
passlib~=1.7.4
+9
View File
@@ -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