diff --git a/requirements/requirements.txt b/requirements/requirements.txt index e3405ce13..50876e74b 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -14,7 +14,7 @@ django-speedinfo==1.4.0 django-extensions==2.1.4 django-image-cropping==1.2 django-webpack-loader==0.6.0 -drf-yasg==1.20.0 +drf-spectacular==0.18.2 django-ratelimit==3.0.1 easy-thumbnails==2.5 python-decouple==3.1 diff --git a/sapl/api/core/__init__.py b/sapl/api/core/__init__.py index 438b993a8..49ddeeb75 100644 --- a/sapl/api/core/__init__.py +++ b/sapl/api/core/__init__.py @@ -101,7 +101,7 @@ class SaplApiViewSetConstrutor(): class Meta(_meta_serializer): if not hasattr(_meta_serializer, 'ref_name'): - ref_name = None + ref_name = f'{object_name}Serializer' if not hasattr(_meta_serializer, 'model'): model = _model @@ -117,7 +117,7 @@ class SaplApiViewSetConstrutor(): else: fields = _meta_serializer.fields - def get___str__(self, obj): + def get___str__(self, obj) -> str: return str(obj) _meta_filterset = object if not hasattr( @@ -129,7 +129,7 @@ class SaplApiViewSetConstrutor(): class Meta(_meta_filterset): if not hasattr(_meta_filterset, 'model'): model = _model - + # Define uma classe padrão ModelViewSet de DRF class ModelSaplViewSet(SaplApiViewSetConstrutor.SaplApiViewSet): queryset = _model.objects.all() diff --git a/sapl/api/core/schema.py b/sapl/api/core/schema.py new file mode 100644 index 000000000..b7ca42354 --- /dev/null +++ b/sapl/api/core/schema.py @@ -0,0 +1,5 @@ +from drf_spectacular.openapi import AutoSchema + + +class Schema(AutoSchema): + pass diff --git a/sapl/api/pagination.py b/sapl/api/pagination.py index d205df3c5..9d1c2f1eb 100644 --- a/sapl/api/pagination.py +++ b/sapl/api/pagination.py @@ -13,6 +13,67 @@ class StandardPagination(pagination.PageNumberPagination): return None return super().paginate_queryset(queryset, request, view=view) + def get_paginated_response_schema(self, schema): + r = { + 'type': 'object', + 'properties': { + 'pagination': { + 'type': 'object', + 'properties': { + 'links': { + 'type': 'object', + 'properties': { + 'next': { + 'type': 'string', + 'nullable': True, + 'format': 'uri', + 'example': 'http://api.example.org/accounts/?{page_query_param}=4'.format( + page_query_param=self.page_query_param) + }, + 'previous': { + 'type': 'string', + 'nullable': True, + 'format': 'uri', + 'example': 'http://api.example.org/accounts/?{page_query_param}=2'.format( + page_query_param=self.page_query_param) + }, + } + }, + 'previous_page': { + 'type': 'integer', + 'example': 123, + }, + 'next_page': { + 'type': 'integer', + 'example': 123, + }, + 'start_index': { + 'type': 'integer', + 'example': 123, + }, + 'end_index': { + 'type': 'integer', + 'example': 123, + }, + 'total_entries': { + 'type': 'integer', + 'example': 123, + }, + 'total_pages': { + 'type': 'integer', + 'example': 123, + }, + 'page': { + 'type': 'integer', + 'example': 123, + }, + } + }, + 'results': schema, + }, + } + return r + def get_paginated_response(self, data): try: previous_page_number = self.page.previous_page_number() @@ -26,6 +87,10 @@ class StandardPagination(pagination.PageNumberPagination): return Response({ 'pagination': { + 'links': { + 'next': self.get_next_link(), + 'previous': self.get_previous_link(), + }, 'previous_page': previous_page_number, 'next_page': next_page_number, 'start_index': self.page.start_index(), diff --git a/sapl/api/urls.py b/sapl/api/urls.py index 2c3b1fd7f..4a23ca00f 100644 --- a/sapl/api/urls.py +++ b/sapl/api/urls.py @@ -1,6 +1,7 @@ -from django.conf import settings + from django.conf.urls import include, url -from rest_framework import permissions +from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView,\ + SpectacularRedocView from rest_framework.routers import DefaultRouter from sapl.api.deprecated import MateriaLegislativaViewSet, SessaoPlenariaViewSet,\ @@ -28,8 +29,17 @@ for app, built_sets in SaplApiViewSetConstrutor._built_sets.items(): urlpatterns_router = router.urls -urlpatterns_api_doc = [] -if 'drf_yasg' in settings.INSTALLED_APPS: +urlpatterns_api_doc = [ + # Optional UI: + url('^schema/swagger-ui/', + SpectacularSwaggerView.as_view(url_name='sapl.api:schema_api'), name='swagger-ui'), + url('^schema/redoc/', + SpectacularRedocView.as_view(url_name='sapl.api:schema_api'), name='redoc'), + # YOUR PATTERNS + url('^schema/', SpectacularAPIView.as_view(), name='schema_api'), + +] +"""if 'drf_yasg' in settings.INSTALLED_APPS: from drf_yasg import openapi from drf_yasg.views import get_schema_view schema_view = get_schema_view( @@ -50,7 +60,7 @@ if 'drf_yasg' in settings.INSTALLED_APPS: schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), url(r'^docs/redoc/$', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), - ] + ]""" # TODO: refatorar para customização da api automática deprecated_urlpatterns_api = [ diff --git a/sapl/settings.py b/sapl/settings.py index 47c0b9be7..638b63abc 100644 --- a/sapl/settings.py +++ b/sapl/settings.py @@ -81,7 +81,7 @@ INSTALLED_APPS = ( 'crispy_forms', 'floppyforms', - 'drf_yasg', + 'drf_spectacular', 'rest_framework', 'rest_framework.authtoken', 'django_filters', @@ -160,12 +160,23 @@ REST_FRAMEWORK = { 'rest_framework.authentication.TokenAuthentication', "rest_framework.authentication.SessionAuthentication", ), + + 'DEFAULT_SCHEMA_CLASS': 'sapl.api.core.schema.Schema', + "DEFAULT_PAGINATION_CLASS": "sapl.api.pagination.StandardPagination", + "DEFAULT_FILTER_BACKENDS": ( "rest_framework.filters.SearchFilter", 'django_filters.rest_framework.DjangoFilterBackend', ), } + +SPECTACULAR_SETTINGS = { + 'TITLE': 'Sapl API - docs', + 'DESCRIPTION': 'Sapl API - Docs', + 'VERSION': '1.0.0', +} + CACHES = { 'default': { 'BACKEND': 'speedinfo.backends.proxy_cache',