Skip to content

feat(backend): generate OpenAPI v3 swagger docs #5259

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions dependencies/pip/dev_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ django==4.2.15
# django-taggit
# django-timezone-field
# djangorestframework
# drf-spectacular
# jsonfield
# model-bakery
django-allauth==65.1.0
Expand Down Expand Up @@ -244,6 +245,7 @@ djangorestframework==3.15.1
# -r dependencies/pip/requirements.in
# djangorestframework-csv
# drf-extensions
# drf-spectacular
djangorestframework-csv==3.0.2
# via -r dependencies/pip/requirements.in
djangorestframework-jsonp==1.0.2
Expand All @@ -258,6 +260,8 @@ docutils==0.20.1
# via statistics
drf-extensions==0.7.1
# via -r dependencies/pip/requirements.in
drf-spectacular==0.27.2
# via -r dependencies/pip/requirements.in
et-xmlfile==1.1.0
# via openpyxl
exceptiongroup==1.2.0
Expand Down Expand Up @@ -352,6 +356,8 @@ idna==3.6
# via
# requests
# yarl
inflection==0.5.1
# via drf-spectacular
iniconfig==2.0.0
# via pytest
invoke==2.2.0
Expand All @@ -373,6 +379,7 @@ jsonfield==3.1.0
jsonschema==4.21.1
# via
# -r dependencies/pip/requirements.in
# drf-spectacular
# formpack
jsonschema-specifications==2023.12.1
# via jsonschema
Expand Down Expand Up @@ -541,7 +548,9 @@ pyxform==3.0.0
# -r dependencies/pip/requirements.in
# formpack
pyyaml==6.0.1
# via responses
# via
# drf-spectacular
# responses
redis==5.0.3
# via
# celery
Expand Down Expand Up @@ -651,7 +660,9 @@ tzdata==2024.1
ua-parser==0.18.0
# via -r dependencies/pip/requirements.in
uritemplate==4.1.1
# via google-api-python-client
# via
# drf-spectacular
# google-api-python-client
urllib3==1.26.18
# via
# botocore
Expand Down
2 changes: 2 additions & 0 deletions dependencies/pip/requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,5 @@ modilabs-python-utils
djangorestframework-csv
djangorestframework-jsonp
pandas

drf-spectacular # OpenAPI v3 schema generator
15 changes: 13 additions & 2 deletions dependencies/pip/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ django==4.2.15
# django-taggit
# django-timezone-field
# djangorestframework
# drf-spectacular
# jsonfield
django-allauth==65.1.0
# via -r dependencies/pip/requirements.in
Expand Down Expand Up @@ -212,6 +213,7 @@ djangorestframework==3.15.1
# -r dependencies/pip/requirements.in
# djangorestframework-csv
# drf-extensions
# drf-spectacular
djangorestframework-csv==3.0.2
# via -r dependencies/pip/requirements.in
djangorestframework-jsonp==1.0.2
Expand All @@ -224,6 +226,8 @@ docutils==0.20.1
# via statistics
drf-extensions==0.7.1
# via -r dependencies/pip/requirements.in
drf-spectacular==0.27.2
# via -r dependencies/pip/requirements.in
et-xmlfile==1.1.0
# via openpyxl
flower==2.0.1
Expand Down Expand Up @@ -296,6 +300,8 @@ idna==3.6
# via
# requests
# yarl
inflection==0.5.1
# via drf-spectacular
isodate==0.6.1
# via azure-storage-blob
jmespath==1.0.1
Expand All @@ -307,6 +313,7 @@ jsonfield==3.1.0
jsonschema==4.21.1
# via
# -r dependencies/pip/requirements.in
# drf-spectacular
# formpack
jsonschema-specifications==2023.12.1
# via jsonschema
Expand Down Expand Up @@ -416,7 +423,9 @@ pyxform==3.0.0
# -r dependencies/pip/requirements.in
# formpack
pyyaml==6.0.1
# via responses
# via
# drf-spectacular
# responses
redis==5.0.3
# via
# celery
Expand Down Expand Up @@ -499,7 +508,9 @@ tzdata==2024.1
ua-parser==0.18.0
# via -r dependencies/pip/requirements.in
uritemplate==4.1.1
# via google-api-python-client
# via
# drf-spectacular
# google-api-python-client
urllib3==1.26.18
# via
# botocore
Expand Down
2 changes: 1 addition & 1 deletion kobo/apps/openrosa/libs/renderers/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def filter_renderers(self, renderers, format):

class MediaFileRenderer(BaseRenderer):
media_type = '*/*'
format = None
format = 'TODO'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

drf_spectacular requires format to be a string, otherwise it crashes. What would the right format here?

charset = None
render_style = 'binary'

Expand Down
21 changes: 8 additions & 13 deletions kobo/apps/reports/serializers.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
# coding: utf-8
from rest_framework import serializers
from rest_framework.reverse import reverse
from kpi.serializers.v2.reports import (
ReportsDetailSerializer as ReportsDetailSerializerV2,
ReportsListSerializer as ReportsListSerializerV2,
)

from kobo.apps.reports import report_data
from kpi.serializers.v2.reports import ReportsDetailSerializer as ReportsDetailSerializerV2

class ReportsDetailSerializer(ReportsDetailSerializerV2):

pass

class ReportsListSerializer(serializers.BaseSerializer):
def to_representation(self, obj):
request = self.context['request']
return {
'url': reverse('reports-detail', args=(obj.uid,), request=request),
}

class ReportsListSerializer(ReportsListSerializerV2):

class ReportsDetailSerializer(ReportsDetailSerializerV2):

pass
13 changes: 10 additions & 3 deletions kobo/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@
'kobo.apps.openrosa.libs',
'kobo.apps.project_ownership.app.ProjectOwnershipAppConfig',
'kobo.apps.long_running_migrations.app.LongRunningMigrationAppConfig',
'drf_spectacular',
)

MIDDLEWARE = [
Expand Down Expand Up @@ -666,9 +667,6 @@
'positive_int_minus_one': ['django.forms.fields.IntegerField', {
'min_value': -1
}],
'positive_int': ['django.forms.fields.IntegerField', {
'min_value': 0
}],
}

CONSTANCE_CONFIG_FIELDSETS = {
Expand Down Expand Up @@ -967,6 +965,15 @@ def __init__(self, *args, **kwargs):
'DEFAULT_VERSIONING_CLASS': 'kpi.versioning.APIAutoVersioning',
# Cannot be placed in kpi.exceptions.py because of circular imports
'EXCEPTION_HANDLER': 'kpi.utils.drf_exceptions.custom_exception_handler',
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
}

SPECTACULAR_SETTINGS = {
'TITLE': 'KoboToolbox API',
'DESCRIPTION': 'Warning: experimental schema generation. Use at your own risk.',
'VERSION': '0.0.1',
'SERVE_INCLUDE_SCHEMA': False,
# OTHER SETTINGS
}

OPENROSA_REST_FRAMEWORK = {
Expand Down
20 changes: 17 additions & 3 deletions kobo/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,29 @@
from django.views.generic.base import RedirectView
from rest_framework import status
from rest_framework.exceptions import server_error
from drf_spectacular.views import (
SpectacularAPIView,
SpectacularRedocView,
SpectacularSwaggerView,
)

from kpi.utils.urls import is_request_for_html

admin.autodiscover()
admin.site.login = staff_member_required(
admin.site.login, login_url=settings.LOGIN_URL
)
admin.site.login = staff_member_required(admin.site.login, login_url=settings.LOGIN_URL)

urlpatterns = [
path('api/schema/', SpectacularAPIView.as_view(api_version='api_v2'), name='schema'),
path(
'api/schema/swagger/',
SpectacularSwaggerView.as_view(url_name='schema'),
name='swagger-ui',
),
path(
'api/schema/redoc/',
SpectacularRedocView.as_view(url_name='schema'),
name='redoc',
),
# https://github.com/stochastic-technologies/django-loginas
re_path(r'^admin/', include('loginas.urls')),
# Disable admin login form
Expand Down
2 changes: 1 addition & 1 deletion kpi/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def filter_queryset(self, request, queryset, view):
return queryset


class KpiObjectPermissionsFilter:
class KpiObjectPermissionsFilter(filters.BaseFilterBackend):

STATUS_PARAMETER = 'status'
PARENT_UID_PARAMETER = 'parent__uid'
Expand Down
2 changes: 1 addition & 1 deletion kpi/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class AssetJsonRenderer(renderers.JSONRenderer):

class MediaFileRenderer(renderers.BaseRenderer):
media_type = '*/*'
format = None
format = 'TODO'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

drf_spectacular requires format to be a string, otherwise it crashes. What would the right format here?

charset = None
render_style = 'binary'

Expand Down
16 changes: 15 additions & 1 deletion kpi/serializers/v2/reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@
from rest_framework.reverse import reverse

from kobo.apps.reports import report_data
from kpi.models import Asset


class ReportsDetailSerializer(serializers.BaseSerializer):
class ReportsDetailSerializer(serializers.ModelSerializer):

class Meta:
model = Asset
fields = '__all__'

def to_representation(self, obj):
request = self.context['request']
Expand All @@ -29,3 +34,12 @@ def to_representation(self, obj):
'count': len(_list),
'list': _list,
}


class ReportsListSerializer(ReportsDetailSerializer):

def to_representation(self, obj):
request = self.context['request']
return {
'url': reverse('reports-detail', args=(obj.uid,), request=request),
}
Loading