From 998ea135b7f67ec6637d806ffd26987f479f6929 Mon Sep 17 00:00:00 2001 From: Jonas Remmert Date: Wed, 5 Jun 2024 22:53:07 +0200 Subject: [PATCH] doc: automate ReST API documentation The django extension drf_spectacular allows to generate OpenAPI schema. Via redoc, this schema can be rendered into a detailed html api documentation. This api documentation is referenced by Sphinx to be available on the GitHub pages documentation. Signed-off-by: Jonas Remmert --- doc/requirements.txt | 2 ++ doc/source/api.rst | 7 ++++ doc/source/conf.py | 12 +++++++ doc/source/index.rst | 1 + doc/tox.ini | 1 + server/django/requirements.txt | 3 ++ .../django/sensordata/management/__init__.py | 0 .../management/commands/__init__.py | 0 .../management/commands/generate_openapi.py | 32 +++++++++++++++++++ .../sensordata/tests/test_restapi_multi.py | 1 - server/django/sensordata/views.py | 1 + server/django/server/settings.py | 14 ++++++++ 12 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 doc/source/api.rst create mode 100644 server/django/sensordata/management/__init__.py create mode 100644 server/django/sensordata/management/commands/__init__.py create mode 100644 server/django/sensordata/management/commands/generate_openapi.py diff --git a/doc/requirements.txt b/doc/requirements.txt index d44cb4a1..92b6c06f 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -2,6 +2,8 @@ Sphinx==7.3.7 sphinx-book-theme==1.1.2 plantuml==0.3.0 sphinxcontrib-plantuml +sphinxcontrib-httpdomain +sphinxcontrib-redoc sphinx-autobuild codespell tox diff --git a/doc/source/api.rst b/doc/source/api.rst new file mode 100644 index 00000000..d336dc97 --- /dev/null +++ b/doc/source/api.rst @@ -0,0 +1,7 @@ +Internal ReST APIs +================== + +.. The following text is only a placeholder in case the api can not be + generated (e.g. for the pdf version) + +The API Documentation is available at https://jonas-rem.github.io/lwm2m_server. diff --git a/doc/source/conf.py b/doc/source/conf.py index 40c871a2..2ffae6b5 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -31,6 +31,7 @@ 'sphinxcontrib.plantuml', 'sphinx.ext.imgconverter', 'sphinx.ext.todo', + 'sphinxcontrib.redoc', ] templates_path = ['_templates'] @@ -45,6 +46,17 @@ 'custom.css', ] +# Automatically generate ReST API documentation +redoc = [ + { + 'spec': '../build/generated/openapi-schema.yaml', + 'page': 'api', + 'embed': True, + }, +] +# Specify a more recent version for API Doc v +redoc_uri = 'https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js' + html_theme_options = { 'repository_url': 'https://github.com/jonas-rem/lwm2m_server', 'repository_branch': 'main', diff --git a/doc/source/index.rst b/doc/source/index.rst index da058809..d835c916 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -30,4 +30,5 @@ Table of Contents :maxdepth: 1 documentation + api weblog diff --git a/doc/tox.ini b/doc/tox.ini index a8339b13..b2cd8a83 100644 --- a/doc/tox.ini +++ b/doc/tox.ini @@ -10,6 +10,7 @@ deps = [testenv:py3-html] commands = python ../server/django/manage.py graph_models sensordata -o source/images/erd.svg + python ../server/django/manage.py generate_openapi -o build/generated/openapi-schema.yaml sphinx-build -E -W --keep-going -b html source build/html [testenv:py3-pdf] diff --git a/server/django/requirements.txt b/server/django/requirements.txt index d0f2d0ed..bc0f1e74 100644 --- a/server/django/requirements.txt +++ b/server/django/requirements.txt @@ -8,3 +8,6 @@ whitenoise==6.6.0 graphviz==0.20.3 pyparsing==3.1.2 pydot==2.0.0 + +# Generation of API documentation +drf-spectacular==0.27.2 diff --git a/server/django/sensordata/management/__init__.py b/server/django/sensordata/management/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/server/django/sensordata/management/commands/__init__.py b/server/django/sensordata/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/server/django/sensordata/management/commands/generate_openapi.py b/server/django/sensordata/management/commands/generate_openapi.py new file mode 100644 index 00000000..f9454164 --- /dev/null +++ b/server/django/sensordata/management/commands/generate_openapi.py @@ -0,0 +1,32 @@ +from django.core.management.base import BaseCommand +from drf_spectacular.generators import SchemaGenerator +import yaml +import os + +class Command(BaseCommand): + help = 'Generates the OpenAPI schema' + + def add_arguments(self, parser): + parser.add_argument( + '-o', + type=str, + help='File path where the OpenAPI schema should be saved', + default='openapi-schema.yaml' + ) + + def handle(self, *args, **options): + output_path = options['o'] + + # Ensure the directory exists + os.makedirs(os.path.dirname(output_path), exist_ok=True) + + # Generate the OpenAPI schema + generator = SchemaGenerator() + schema = generator.get_schema(request=None, public=True) + schema_yaml = yaml.dump(schema, default_flow_style=False) + + # Write the schema to the specified file + with open(output_path, 'w') as file: + file.write(schema_yaml) + + self.stdout.write(self.style.SUCCESS(f'Exported OpenAPI schema to {output_path}')) diff --git a/server/django/sensordata/tests/test_restapi_multi.py b/server/django/sensordata/tests/test_restapi_multi.py index 82a6f6b5..22cd7a83 100644 --- a/server/django/sensordata/tests/test_restapi_multi.py +++ b/server/django/sensordata/tests/test_restapi_multi.py @@ -7,7 +7,6 @@ TEST_PAYLOAD = [ { "ep": "qemu_x86", - "res": "3", "val": { "instances": [ { diff --git a/server/django/sensordata/views.py b/server/django/sensordata/views.py index f327ffb4..82a67272 100644 --- a/server/django/sensordata/views.py +++ b/server/django/sensordata/views.py @@ -8,6 +8,7 @@ class PostResourceView(APIView): + serializer_class = LwM2MSerializer def post(self, request): serializer = LwM2MSerializer(data=request.data, many=False) diff --git a/server/django/server/settings.py b/server/django/server/settings.py index dfa2ea23..b5e97e40 100644 --- a/server/django/server/settings.py +++ b/server/django/server/settings.py @@ -76,6 +76,7 @@ "django.contrib.staticfiles", "django_extensions", "rest_framework", + "drf_spectacular", "sensordata", ] @@ -90,6 +91,19 @@ 'whitenoise.middleware.WhiteNoiseMiddleware', # Add this ] +REST_FRAMEWORK = { + 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema', +} + +# Automatic ReST API documentation +SPECTACULAR_SETTINGS = { + 'TITLE': 'Leshan ReST API', + 'DESCRIPTION': 'The leshan_api is hosted by Django. The API allows the\ + Leshan server to add LwM2M resource data to Django. It is \ + an internal API and must not be exposed to the internet.', + 'VERSION': '1.0.0', +} + ROOT_URLCONF = "server.urls" TEMPLATES = [