Skip to content
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

OpenAPI schema generation #292

Open
rbuffat opened this issue Dec 23, 2022 · 2 comments
Open

OpenAPI schema generation #292

rbuffat opened this issue Dec 23, 2022 · 2 comments

Comments

@rbuffat
Copy link

rbuffat commented Dec 23, 2022

What is the recommended way to automatically generate an OpenAPI schema of the knox API endpoints? Neither the in the Django REST framework integrated schema builder (python manage.py generateschema) nor drf-spectacular seem to work out of the box.

Django==4.1.4 
psycopg2-binary==2.9.5
djangorestframework==3.14.0
django-cors-headers==3.13.0
django-rest-knox==4.2.0
cryptography==38.0.4
markdown==3.4.1
django-filter==22.1
pyyaml==6.0
uritemplate==4.1.1
coreapi==2.3.3
drf-spectacular==0.25.1 

generateschema:

  /api/login/:
    post:
      operationId: createLogin
      description: ''
      parameters: []
      requestBody:
        content:
          application/json:
            schema: {}
          application/x-www-form-urlencoded:
            schema: {}
          multipart/form-data:
            schema: {}
      responses:
        '201':
          content:
            application/json:
              schema: {}
          description: ''
      tags:
      - api
  /api/logout/:
    post:
      operationId: createLogout
      description: ''
      parameters: []
      requestBody:
        content:
          application/json:
            schema: {}
          application/x-www-form-urlencoded:
            schema: {}
          multipart/form-data:
            schema: {}
      responses:
        '201':
          content:
            application/json:
              schema: {}
          description: ''
      tags:
      - api
  /api/logoutall/:
    post:
      operationId: createLogoutAll
      description: 'Log the user out of all sessions

        I.E. deletes all auth tokens for the user'
      parameters: []
      requestBody:
        content:
          application/json:
            schema: {}
          application/x-www-form-urlencoded:
            schema: {}
          multipart/form-data:
            schema: {}
      responses:
        '201':
          content:
            application/json:
              schema: {}
          description: ''
      tags:
      - api

spectacular

/home/worker/django/api/views.py:8: Error [LoginView]: unable to guess serializer. This is graceful fallback handling for APIViews. Consider using GenericAPIView as view base class, if view is under your control. Either way you may want to add a serializer_class (or method). Ignoring view for now.
/home/worker/django/api/views.py:8: Warning [LoginView]: could not resolve authenticator <class 'knox.auth.TokenAuthentication'>. There was no OpenApiAuthenticationExtension registered for that class. Try creating one by subclassing it. Ignoring for now.
/usr/local/lib/python3.11/site-packages/knox/views.py:70: Error [LogoutView]: unable to guess serializer. This is graceful fallback handling for APIViews. Consider using GenericAPIView as view base class, if view is under your control. Either way you may want to add a serializer_class (or method). Ignoring view for now.
/usr/local/lib/python3.11/site-packages/knox/views.py:70: Warning [LogoutView]: could not resolve authenticator <class 'knox.auth.TokenAuthentication'>. There was no OpenApiAuthenticationExtension registered for that class. Try creating one by subclassing it. Ignoring for now.
/usr/local/lib/python3.11/site-packages/knox/views.py:81: Error [LogoutAllView]: unable to guess serializer. This is graceful fallback handling for APIViews. Consider using GenericAPIView as view base class, if view is under your control. Either way you may want to add a serializer_class (or method). Ignoring view for now.
/usr/local/lib/python3.11/site-packages/knox/views.py:81: Warning [LogoutAllView]: could not resolve authenticator <class 'knox.auth.TokenAuthentication'>. There was no OpenApiAuthenticationExtension registered for that class. Try creating one by subclassing it. Ignoring for now.

...

  /api/login/:
    post:
      operationId: api_login_create
      tags:
      - api
      security:
      - {}
      responses:
        '200':
          description: No response body
  /api/logout/:
    post:
      operationId: api_logout_create
      tags:
      - api
      responses:
        '200':
          description: No response body
  /api/logoutall/:
    post:
      operationId: api_logoutall_create
      description: |-
        Log the user out of all sessions
        I.E. deletes all auth tokens for the user
      tags:
      - api
      responses:
        '200':
          description: No response body


@ianepperson
Copy link

ianepperson commented Jan 27, 2023

I was able to do this with the Login view by overriding it. (Should probably make this a pull request).
First, needed to build a LoginResponseSerializer class (Knox doesn't use one for the login view):

from knox.serializers import UserSerializer as LoginUserSerializer # or whatever serializer you use

# Serializer to mimic the response from a Knox login
class LoginResponseSerializer(serializers.Serializer):
    token = serializers.CharField()
    expiry = serializers.DateTimeField()
    user = LoginUserSerializer()

Then extend the schema for the login view:

from drf_spectacular.utils import extend_schema
from rest_framework.authtoken.serializers import AuthTokenSerializer

class LoginView(KnoxLoginView):
    """
    Login to the API
    This endpoint permits a user to login and use the API.
    """
    @extend_schema(
        request=AuthTokenSerializer, responses={200: LoginResponseSerializer}
    )
    def post(self, request, format=None):
        return super().post(request, format=format)

I've added quite a bit more to my login view so I'm not sure if this works exactly as I've written it, but it should get you going in the right direction at least.

@giovannicimolin
Copy link
Contributor

giovannicimolin commented Sep 8, 2023

Moving my comments from #310 here.

I've recently started using this with drf-spetacular and had to describe some of the API using extensions.
I'll evaluate if it's possible to modify this library in a way that the schema is automatically generated without needing any overrides. (it looks like it's possible and simple from #292 (comment)).

Ping me if you want to take a stab at this! I can review/merge.

If anyone is also trying to use this with drf-spetacular and code generators, this is what I'm using now:

from drf_spectacular.extensions import (
    OpenApiAuthenticationExtension,
    OpenApiViewExtension,
)
from rest_framework import serializers


class KnoxAuthentication(OpenApiAuthenticationExtension):
    """
    Knox authentication Open API definition.
    """

    target_class = "knox.auth.TokenAuthentication"
    name = "TokenAuthentication"

    def get_security_definition(self, auto_schema):
        """
        Custom definition for APIView.
        """
        return {
            "type": "apiKey",
            "in": "header",
            "name": "Authorization",
        }


class LogoutResponseSerializer(serializers.Serializer):
    """
    Empty logout response serializer
    """


class FixLogoutView(OpenApiViewExtension):
    target_class = "knox.views.LogoutView"

    def view_replacement(self):
        """
        Fix view
        """

        class Fixed(self.target_class):
            serializer_class = LogoutResponseSerializer

        return Fixed


class FixLogoutAllView(OpenApiViewExtension):
    target_class = "knox.views.LogoutAllView"

    def view_replacement(self):
        """
        Fix view
        """

        class Fixed(self.target_class):
            serializer_class = LogoutResponseSerializer

        return Fixed

This doesn't yield a 1 to 1 to the API output response, but it doesn't matter for my usage.

Note: be mindful of the settings if you are re-using this, they are specific to my projects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants