Skip to content

Commit

Permalink
add swagger for CMS API via drf-spectacular (openedx#33282)
Browse files Browse the repository at this point in the history
This adds two new urls: `<studio-base>/cms-api/ui` and `<studio-base>/cms-api/schema` with swagger ui and swaggerfile only for the new CMS API using drf-spectacular
  • Loading branch information
jesperhodge authored Sep 19, 2023
1 parent 6714062 commit f197f9e
Show file tree
Hide file tree
Showing 22 changed files with 184 additions and 98 deletions.
2 changes: 1 addition & 1 deletion cms/djangoapps/contentstore/asset_storage_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,7 @@ def _get_thumbnail_asset_key(asset, course_key):


# TODO: this method needs improvement. These view decorators should be at the top in an actual view method,
# but this is just a method called by the asset_handler. The asset_handler used by the public studio content API
# but this is just a method called by the asset_handler. The asset_handler used by the public CMS API
# just ignores all of this stuff.
@require_http_methods(('DELETE', 'POST', 'PUT'))
@login_required
Expand Down
15 changes: 15 additions & 0 deletions cms/djangoapps/contentstore/rest_api/v1/serializers/xblock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""
API Serializers for xblocks
"""


# TODO: implement and use serializer
# from rest_framework import serializers

# class XblockSerializer(serializers.Serializer):
# """Serializer for xblocks"""
# id=serializers.CharField(required=False)
# display_name=serializers.CharField(required=False)
# category=serializers.CharField(required=False)
# data=serializers.CharField(required=False)
# metadata=serializers.DictField(required=False)
4 changes: 2 additions & 2 deletions cms/djangoapps/contentstore/rest_api/v1/views/assets.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
Public rest API endpoints for the Studio Content API Assets.
Public rest API endpoints for the CMS API Assets.
"""
import logging
from rest_framework.generics import RetrieveUpdateDestroyAPIView, CreateAPIView
Expand All @@ -21,7 +21,7 @@
@view_auth_classes()
class AssetsView(DeveloperErrorViewMixin, RetrieveUpdateDestroyAPIView, CreateAPIView):
"""
public rest API endpoints for the Studio Content API Assets.
public rest API endpoints for the CMS API Assets.
course_key: required argument, needed to authorize course authors and identify the asset.
asset_key_string: required argument, needed to identify the asset.
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@


"""
Tests for the xblock view of the Studio Content API. This tests only the view itself,
Tests for the xblock view of the CMS API. This tests only the view itself,
not the underlying Xblock service.
It checks that the assets_handler method of the Xblock service is called with the expected parameters.
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
Tests for the xblock view of the Studio Content API. This tests only the view itself,
Tests for the xblock view of the CMS API. This tests only the view itself,
not the underlying Xblock service.
It checks that the xblock_handler method of the Xblock service is called with the expected parameters.
"""
Expand Down
4 changes: 2 additions & 2 deletions cms/djangoapps/contentstore/rest_api/v1/views/transcripts.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
Public rest API endpoints for the Studio Content API video assets.
Public rest API endpoints for the CMS API video assets.
"""
import logging
from rest_framework.generics import (
Expand Down Expand Up @@ -29,7 +29,7 @@
@view_auth_classes()
class TranscriptView(DeveloperErrorViewMixin, CreateAPIView, RetrieveAPIView, DestroyAPIView):
"""
public rest API endpoints for the Studio Content API video transcripts.
public rest API endpoints for the CMS API video transcripts.
course_key: required argument, needed to authorize course authors and identify the video.
edx_video_id: optional query parameter, needed to identify the transcript.
language_code: optional query parameter, needed to identify the transcript.
Expand Down
4 changes: 2 additions & 2 deletions cms/djangoapps/contentstore/rest_api/v1/views/videos.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
Public rest API endpoints for the Studio Content API video assets.
Public rest API endpoints for the CMS API video assets.
"""
import logging
from rest_framework.generics import (
Expand Down Expand Up @@ -31,7 +31,7 @@
@view_auth_classes()
class VideosView(DeveloperErrorViewMixin, CreateAPIView, RetrieveAPIView, DestroyAPIView):
"""
public rest API endpoints for the Studio Content API video assets.
public rest API endpoints for the CMS API video assets.
course_key: required argument, needed to authorize course authors and identify the video.
video_id: required argument, needed to identify the video.
"""
Expand Down
12 changes: 8 additions & 4 deletions cms/djangoapps/contentstore/rest_api/v1/views/xblock.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
Public rest API endpoints for the Studio Content API.
Public rest API endpoints for the CMS API.
"""
import logging
from rest_framework.generics import RetrieveUpdateDestroyAPIView, CreateAPIView
Expand All @@ -9,11 +9,13 @@
from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin, view_auth_classes
from common.djangoapps.util.json_request import expect_json_in_class_view

from ....api import course_author_access_required

from cms.djangoapps.contentstore.api import course_author_access_required
from cms.djangoapps.contentstore.xblock_storage_handlers import view_handlers
import cms.djangoapps.contentstore.toggles as contentstore_toggles

# from cms.djangoapps.contentstore.rest_api.v1.serializers import XblockSerializer


log = logging.getLogger(__name__)
toggles = contentstore_toggles
handle_xblock = view_handlers.handle_xblock
Expand All @@ -22,11 +24,13 @@
@view_auth_classes()
class XblockView(DeveloperErrorViewMixin, RetrieveUpdateDestroyAPIView, CreateAPIView):
"""
Public rest API endpoints for the Studio Content API.
Public rest API endpoints for the CMS API.
course_key: required argument, needed to authorize course authors.
usage_key_string (optional):
xblock identifier, for example in the form of "block-v1:<course id>+type@<type>+block@<block id>"
"""
# TODO: uncomment next line after XblockSerializer is implemented
# serializer_class = XblockSerializer

def dispatch(self, request, *args, **kwargs):
# TODO: probably want to refactor this to a decorator.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ def handle_xblock(request, usage_key_string=None):
"""
Service method with all business logic for handling xblock requests.
This method is used both by the internal xblock_handler API and by
the public studio content API.
the public CMS API.
"""
if usage_key_string:
usage_key = usage_key_with_run(usage_key_string)
Expand Down
16 changes: 16 additions & 0 deletions cms/envs/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,9 @@

# Blockstore
'blockstore.apps.bundles',

# alternative swagger generator for CMS API
'drf_spectacular',
]


Expand Down Expand Up @@ -2771,3 +2774,16 @@
#### django-simple-history##
# disable indexing on date field its coming django-simple-history.
SIMPLE_HISTORY_DATE_INDEX = False

# This affects the CMS API swagger docs but not the legacy swagger docs under /api-docs/.
REST_FRAMEWORK['DEFAULT_SCHEMA_CLASS'] = 'drf_spectacular.openapi.AutoSchema'

# These fields override the spectacular settings default values.
# Any fields not included here will use the default values.
SPECTACULAR_SETTINGS = {
'TITLE': 'CMS API',
'DESCRIPTION': 'Experimental API to edit xblocks and course content. Danger: Do not use on running courses!',
'VERSION': '1.0.0',
'SERVE_INCLUDE_SCHEMA': False,
'PREPROCESSING_HOOKS': ['cms.lib.spectacular.cms_api_filter'], # restrict spectacular to CMS API endpoints
}
19 changes: 19 additions & 0 deletions cms/lib/spectacular.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
""" Helper functions for drf-spectacular """


def cms_api_filter(endpoints):
"""
At the moment, we are only enabling drf-spectacular for the CMS API.
Filter out endpoints that are not part of the CMS API.
"""
filtered = []
for (path, path_regex, method, callback) in endpoints:
# Add only paths to the list that are part of the CMS API
if (
path.startswith("/api/contentstore/v1/xblock") or
path.startswith("/api/contentstore/v1/videos") or
path.startswith("/api/contentstore/v1/video_transcripts") or
path.startswith("/api/contentstore/v1/file_assets")
):
filtered.append((path, path_regex, method, callback))
return filtered
9 changes: 8 additions & 1 deletion cms/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
from django.urls import include
from django.urls import path, re_path
from django.utils.translation import gettext_lazy as _
from django.contrib import admin
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
from auth_backends.urls import oauth2_urlpatterns
from edx_api_doc_tools import make_docs_urls
from django.contrib import admin

import openedx.core.djangoapps.common_views.xblock
import openedx.core.djangoapps.debug.views
Expand Down Expand Up @@ -340,3 +341,9 @@
urlpatterns += [
path('api/content_tagging/', include(('openedx.features.content_tagging.urls'))),
]

# studio-content-api specific API docs (using drf-spectacular and openapi-v3)
urlpatterns += [
re_path('^cms-api/ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
re_path('^cms-api/schema/', SpectacularAPIView.as_view(), name='schema'),
]
8 changes: 4 additions & 4 deletions requirements/edx-sandbox/py38.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ click==8.1.6
# nltk
codejail-includes==1.0.0
# via -r requirements/edx-sandbox/py38.in
contourpy==1.1.0
contourpy==1.1.1
# via matplotlib
cryptography==38.0.4
# via
Expand All @@ -38,7 +38,7 @@ markupsafe==2.1.3
# via
# chem
# openedx-calc
matplotlib==3.7.2
matplotlib==3.7.3
# via -r requirements/edx-sandbox/py38.in
mpmath==1.3.0
# via sympy
Expand All @@ -65,7 +65,7 @@ pillow==9.5.0
# matplotlib
pycparser==2.21
# via cffi
pyparsing==3.0.9
pyparsing==3.1.1
# via
# -r requirements/edx-sandbox/py38.in
# chem
Expand Down Expand Up @@ -94,5 +94,5 @@ sympy==1.12
# openedx-calc
tqdm==4.66.1
# via nltk
zipp==3.16.2
zipp==3.17.0
# via importlib-resources
Loading

0 comments on commit f197f9e

Please sign in to comment.