Next steps for API docs

Last week I merged my pull request that puts some bare-bones API doc tooling in place on the edx-platform repo: https://github.com/edx/edx-platform/pull/21207

What should we tackle next? A few people had concerns about the decorator syntax. Who wants to take a stab at another way to do it?

Here are smaller issues to investigate:

  • OpenAPI v2 has a global “tags” section. This is where we could write text that applies to all of the “bookmarks” endpoints, for example. There doesn’t seem to be a way to write the text for those tags sections. (axnsan12/drf-yasg#454)
  • If you omit the “responses” (for example, as BookmarksListView is now), then the Swagger UI shows a Bookmark response type, but the swagger.yaml output has no type. (axnsan12/drf-yasg#449)
  • Multi-line “help_text” strings are not dedented, for example in BookmarkSerializer. This means they appear differently in Markdown output.
  • This traceback needs to be investigated:
    2019-09-16 17:11:18,976 WARNING 929 [drf_yasg.inspectors.base] [user 9] base.py:54 - view's CourseUpdatesList raised exception during schema generation; use `getattr(self, 'swagger_fake_view', False)` to detect and short-circuit this
    Traceback (most recent call last):
      File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/drf_yasg/inspectors/base.py", line 50, in call_view_method
        return view_method()
      File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/rest_framework/generics.py", line 110, in get_serializer
        serializer_class = self.get_serializer_class()
      File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/rest_framework/generics.py", line 127, in get_serializer_class
        % self.__class__.__name__
    AssertionError: 'CourseUpdatesList' should either include a `serializer_class` attribute, or override the `get_serializer_class()` method.
  • BookmarkPaginator’s fields aren’t all represented in the Swagger UI. Why not?
  • I don’t know how to add descriptions to path arguments like CertificatesListView’s username parameter.
  • Ad-hoc responses, like CertificatesListView.get are not handled well.
  • We need to include the Studio endpoints also. As a separate swagger.yaml I guess?

Making progress on any of this would be helpful. Who has opinions, or energy to put into it?

Ned, I would like to propose a change regarding the decorator syntax. Please checkout my branch here: https://github.com/edx/edx-platform/compare/master...regisb:regisb/simplify-swagger-auto-schema?expand=1
Basically, this PR uses the __doc__ attribute of the view functions to populate the operation_summary and operation_description arguments. I think this is an acceptable compromise, where we still use the decorators to declare method properties, but we also make use of the more natural docstrings to document the views.
If you think this is acceptable, I’ll open a PR.

2 Likes

@regis I like the idea. I think @kmccormick was also mulling over something like this, but that would also support the other structured information in the decorator. Let’s get the ideas on the table and decide what to do.

Cool, I’ll open a PR then.

EDIT: Here it is https://github.com/edx/edx-platform/pull/21816

Here is a Draft PR that builds on top of @regis’s: https://github.com/edx/edx-platform/pull/21820

It essentially would allow:

@swagger_auto_schema(
    manual_parameters=[
        openapi.Parameter(
           'course_id',
            openapi.IN_QUERY,
            type=openapi.TYPE_STRING,
            description="The id of the course to limit the list",
        ),
        openapi.Parameter(
            'fields',
            openapi.IN_QUERY,
            type=openapi.TYPE_STRING,
            description="The fields to return: display_name, path.",
        ),
    ],
)
def get(self, request, *args, **kwargs):
        ...

to be written as:

@openapi.schema
@openapi.query_parameter(
    'course_id',
    type=openapi.TYPE_STRING,
    description="The id of the course to limit the list",
)
@openapi.query_parameter(
    'fields',
    type=openapi.TYPE_STRING,
    description="The fields to return: display_name, path.",
)
def get(self, request, *args, **kwargs):
    ...

All thoughts and feedback are appreciated.

I definitely like where this is going. Any concerns?