Skip to content

Array Schema from OpenApiSerializerExtension not included in the result #391

@mohannad-musleh

Description

@mohannad-musleh

Describe the bug
when using OpenApiSerializerExtension that returns schema component with array element as the root, the component will not be included in the final open API schema json.

To Reproduce

extension implementation

from marshmallow import Schema
from drf_spectacular.extensions import OpenApiSerializerExtension

class MarshmallowSerializerExtension(OpenApiSerializerExtension):
    target_class = Schema
    match_subclasses = True
    
    def get_name(self):
        if inspect.isclass(self.target):
            serializer_name = self.target.__name__
        else:
            serializer_name = type(self.target).__name__

        return serializer_name
        
   def map_serializer(self, auto_schema, direction):
       return { # this will not be included in the final result (in components object)
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "e1": {
                            "type": "string",
                            "enum": ["a", "b", "c"]
                        },
                    },
                    "xml": {
                        "name": "item"
                    }
                },
                "xml": {
                    "wrapped": True,
                    "name": "arrayresponse"
                }
            }

ViewSet implementation

note: I'm using OpenApiViewExtension, but the issue same as if I use @extend_schema directly in the ViewSet class implementation

from rest_framework.viewsets import ViewSet
from rest_framework_xml.parsers import XMLParser
from rest_framework_xml.renderers import XMLRenderer

class MyViewSet(ViewSet):
    parser_classes = (XMLParser,)
    renderer_classes = (XMLRenderer,)

    def post(self, request, format=None):
        # ...
        pass

ViewSet open api view extension implementation

from drf_spectacular.utils import extend_schema
from drf_spectacular.extensions import OpenApiViewExtension

class MyViewSetExtension(OpenApiViewExtension):
    target_class = 'my_project.views.MyViewSet'
    
    def view_replacement(self):
        class Fixer(self.target_class):
            @extend_schema(request=RequestSerializer) # RequestSerializer as a class that will be handled by MarshmallowSerializerExtension
            def create(self, request):
                pass

Generated schema

notice the components object is empty

{
    "openapi": "3.0.3",
    "info": {
        "title": "My App",
        "version": "1.0",
        "description": "My App APIs documentation"
    },
    "paths": {
        "/api/v1/my_view": {
            "post": {
                "operationId": "my_view_create",
                "description": "The API description",
                "summary": "The API summary",
                "tags": [
                    "my_view"
                ],
                "responses": {
                    "200": {
                        "description": "No response body"
                    }
                }
            }
        }
    },
    "components": {}
}

Expected behavior
I expect the component to be included in the generated schema

expected schema

{
    "openapi": "3.0.3",
    "info": {
        "title": "My App",
        "version": "1.0",
        "description": "My App APIs documentation"
    },
    "paths": {
        "/api/v1/my_view": {
            "post": {
                "operationId": "my_view_create",
                "description": "The API description",
                "summary": "The API summary",
                "tags": [
                    "my_view"
                ],
                "requestBody": {
                    "content": {
                        "application/xml": {
                            "schema": {
                                "$ref": "#/components/schemas/RequestSchema"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "No response body"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "RequestSchema": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "e1": {
                            "type": "string",
                            "enum": [
                                "a",
                                "b",
                                "c"
                            ]
                        }
                    },
                    "xml": {
                        "name": "item"
                    }
                },
                "xml": {
                    "wrapped": true,
                    "name": "arrayresponse"
                }
            }
        }
    }
}

More details

  • Python version: 3.7.5
  • Installed Packages:
    • Django==2.2.16
    • marshmallow==3.3.0
    • djangorestframework==3.10.3
    • djangorestframework-xml==2.0.0
    • drf-spectacular==0.15.1

I did some investigation in the library implementation and this part that causes the issue

# drf_spectacular/openapi.py

class AutoSchema(ViewInspector):
    def resolve_serializer(self, serializer, direction) -> ResolvedComponent:
        # ...
        keep_component = (
            or any(nest_tag in component.schema for nest_tag in ['oneOf', 'allOf', 'anyOf'])
            or component.schema.get('properties', {})
        )

        if not keep_component:
            del self.registry[component]
            return ResolvedComponent(None, None)  # sentinel
       # ...

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingfix confirmation pendingissue has been fixed and confirmation from issue reporter is pending

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions