Skip to content

Commit

Permalink
Add API docs generation to Figures devsite
Browse files Browse the repository at this point in the history
OpenAPI/Swagger documentation generation now included in Figures
devsite. This requires running Figures devsite in "Juniper mode" with
Python 3.8 (3.5 might work but not tested).

See the file: `docs/soruce/api.rst` for details

Also added some improvements to the environment settings

* Moved the `.env` location to the same directory as `devsite/manage.py`
isntead of being in the devsite main package.
* Updated `.env.example`
* Added more settings to be enabled by `.env`

See the comments in `.env.example` for details
  • Loading branch information
johnbaldwin committed Dec 6, 2020
1 parent a4546bc commit 0b75400
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 20 deletions.
34 changes: 34 additions & 0 deletions devsite/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#
# Figures devsite environment settings example file
#

# Django development debug mode
DEBUG=true

# Set which expected Open edX release mocks for devsite to use.
# Valid options are: "GINKGO", "HAWTHORN", "JUNIPER"
#
# If not specified here, then "HAWTHORN" is used
OPENEDX_RELEASE=JUNIPER

# Enable/disable Figures multisite mode in devsite
# This also requires
FIGURES_IS_MULTISITE=true

# Core Django setting to set which allowed hosts/domain names that devsite can serve.
# See: https://docs.djangoproject.com/en/2.2/ref/settings/#allowed-hosts
# The primary purpose of this setting to help with multi-site development.
# Example
# ALLOWED_HOSTS=*,alpha.localhost, bravo.localhost
ALLOWED_HOSTS=*

# Set the log level that devsite uses
# Log levels: Critical=50, Error=40, Warning=30, Info=20, Debug=10, Notset=0
LOG_LEVEL=10

# Enable the OpenAPI docs feature
ENABLE_OPENAPI_DOCS=true

# Set synthetic data seed options
SEED_DAYS_BACK=60
SEED_NUM_LEARNERS_PER_COURSE=25
8 changes: 0 additions & 8 deletions devsite/devsite/.env.example

This file was deleted.

24 changes: 15 additions & 9 deletions devsite/devsite/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,20 @@
update_celerybeat_schedule,
)

DEVSITE_BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
PROJECT_ROOT_DIR = os.path.dirname(DEVSITE_BASE_DIR)

env = environ.Env(
DEBUG=(bool, True),
OPENEDX_RELEASE=(str, 'HAWTHORN'),
FIGURES_IS_MULTISITE=(bool, False),
ENABLE_DEVSITE_CELERY=(bool, True),
ENABLE_OPENAPI_DOCS=(bool, False),
SEED_DAYS_BACK=(int, 60),
SEED_NUM_LEARNERS_PER_COURSE=(int, 25),
OPENEDX_RELEASE=(str, 'HAWTHORN'),
ENABLE_DEVSITE_CELERY=(bool, True)
)

environ.Env.read_env()
environ.Env.read_env(os.path.join(DEVSITE_BASE_DIR, '.env'))

OPENEDX_RELEASE = env('OPENEDX_RELEASE').upper()
ENABLE_DEVSITE_CELERY = env('ENABLE_DEVSITE_CELERY')
Expand All @@ -35,23 +39,20 @@

MOCKS_DIR = 'mocks/{}'.format(OPENEDX_RELEASE.lower())

DEVSITE_BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
PROJECT_ROOT_DIR = os.path.dirname(DEVSITE_BASE_DIR)

# SECURITY WARNING: Use a real key when running in the cloud and keep it secret
SECRET_KEY = 'insecure-secret-key'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
DEBUG = env('DEBUG')
USE_TZ = True
TIME_ZONE = 'UTC'
ALLOWED_HOSTS = []
ALLOWED_HOSTS = env('ALLOWED_HOSTS')

# Set the default Site (django.contrib.sites.models.Site)
SITE_ID = 1

# TODO: Update this to allow environment variable override
# ENABLE_DEVSITE_CELERY = True
ENABLE_DEVSITE_CELERY = env('ENABLE_DEVSITE_CELERY')

# Adds the mock edx-platform modules to the Python module search path
sys.path.append(os.path.normpath(os.path.join(PROJECT_ROOT_DIR, MOCKS_DIR)))
Expand Down Expand Up @@ -249,3 +250,8 @@
'DAYS_BACK': env('SEED_DAYS_BACK'),
'NUM_LEARNERS_PER_COURSE': env('SEED_NUM_LEARNERS_PER_COURSE')
}

ENABLE_OPENAPI_DOCS = env('ENABLE_OPENAPI_DOCS')

if ENABLE_OPENAPI_DOCS:
INSTALLED_APPS += ['drf_yasg2']
34 changes: 31 additions & 3 deletions devsite/devsite/urls.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'''
URL patterns includes for Figures devsite
'''
"""URL patterns includes for Figures development website
"""

from __future__ import absolute_import
from django.conf.urls import include, url
Expand All @@ -15,6 +14,35 @@
url(r'^figures/', include(('figures.urls', 'figures'), namespace='figures')),
]

if settings.ENABLE_OPENAPI_DOCS:
from rest_framework import permissions
from drf_yasg2.views import get_schema_view
from drf_yasg2 import openapi
schema_view = get_schema_view(
openapi.Info(
title="Figures API",
default_version='v1',
description="Figures devsite API",
terms_of_service="https://www.google.com/policies/terms/",
contact=openapi.Contact(email="[email protected]"),
license=openapi.License(name="BSD License"),
),
public=True,
permission_classes=[permissions.AllowAny],
)
urlpatterns += [
url(r'^api-docs(?P<format>\.json|\.yaml)$',
schema_view.without_ui(cache_timeout=0),
name='schema-json'),
url(r'^api-docs/$',
schema_view.with_ui('swagger', cache_timeout=0),
name='schema-swagger-ui'),
url(r'^redoc/$',
schema_view.with_ui('redoc', cache_timeout=0),
name='schema-redoc'),
]


if settings.DEBUG:
import debug_toolbar
urlpatterns += [
Expand Down
7 changes: 7 additions & 0 deletions devsite/requirements/development_juniper.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Requirements for developer environment with Python 3.8+

-r juniper_community.txt

# Used to serve OpenAPI docs
# See Figures docs/source/api.rst
drf-yasg2==1.19.4
70 changes: 70 additions & 0 deletions docs/source/api.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
.. _figures_api:

###########
Figures API
###########

************
Architecture
************

Figures has a Django REST Framework (DRF) based REST API.

The architecture used is mostly ``ViewSet`` based. For URL dispatching, the DRF ``DefaultRouter`` is used in ``figures.urls``


The Django REST Framework version used is dictated by the instance of Open edX in which Figures runs:

* For Ginkgo: ``djangorestframework==3.2.3``
* For Hawthorn: ``djangorestframework==3.6.3``
* For Juniper: ``djangorestframework==3.9.4``

Up to and including 0.4, the primary consumer of Figures API has been the frontend UI. Therefore versioning of the API has not been required. We plan to implement API versioning for Figures 0.5.

*****************
API Documentation
*****************

For Figures 0.4, we are beginning our effort to prepare the APIs for remote client use. With this is our intial effort to provide API documentation by generating OpenAPI 2.0 specification docs.


To enable API documenation generation on Figures devsite, you will need to run Figures in Juniper mode using Python 3.8, install the required Python package(s) and enable the OpenAPI settings flag.

1. Create or instantiate a Python 3.8 virtual env
2. Run ``pip install -r devsite/requirements/development_juniper.txt``
3. Set the following in ``figures/devsite/.env`` file::

OPENEDX_RELEASE=JUNIPER
ENABLE_OPENAPI_DOCS=true


Now you can start devsite.

1. Go to the ``devsite`` directory and run ``./manage.py migrate``
2. Run ``./manage.py createsuperuser`` to create an account, as the API requires authorized access
3. Start the web server: ``./manage.py runserver``
4. Open a browser and navigate to the main page, ``http://localhost:8000``
5. Login with the credentials you created in step 2
6. Navigate to one of the following URLs::

http://localhost:8000/api-docs/

http://localhost:8000/api-docs.json

http://localhost:8000/redoc/

Now we have basic API documentation generation, the next phase is to update Figures views docstrings to make the documenation more usable.

============
OpenAPI Docs
============

Figures 0.4 uses `DRF - Yet another Swagger generator 2 <https://github.com/JoelLefkowitz/drf-yasg>`_, version ``1.19.4`` for the intial documentation approach.

---------------
Some Background
---------------

``drf-yasg2`` is a fork from ``drf-yasg``. According to the ``drf-yasg2`` Github repo README, It was forked because ``drf-yasg`` was not being maintained. The last ``drf-yasg`` version was 1.17.1. Work resumed and ``drf-yasg`` has a new ``1.20.0`` version. See the `release history <https://pypi.org/project/drf-yasg/#history>`__.

So, why use ``drf-yasg2 1.9.4`` instead of ``drf-yasg 1.20``? Because ``drf-yasg 1.20.0`` requires DRF 3.10 or greater and Juniper uses DRF version 3.9.4.

0 comments on commit 0b75400

Please sign in to comment.