vitalik / django-ninja

💨 Fast, Async-ready, Openapi, type hints based framework for building APIs
https://django-ninja.dev
MIT License
7.05k stars 422 forks source link

`get_openapi_schema` outputs types using `anyOf` style - doesn't work with the Swift OpenAPI generator #1151

Open IanHoar opened 5 months ago

IanHoar commented 5 months ago

Hey there,

I'm working with a client team that is using django-ninja to build their API. They have handed me a schema.json file that I've converted to yaml and I'm trying to import into my Swift project. When I generate the client side code using the Swift OpenAPI Generator I get errors on every instance where I see this syntax:

my_property:
  anyOf:
    - type: string
    - type: 'null'

Hand converting to this syntax throughout the entire spec allows the generation to succeed:

my_property:
  type: [string, 'null']

My question is this: Is there a way to have django-ninja output this updated type style? Any information to help me wrap my head around this issue would be helpful

benjaoming commented 4 months ago

Check out https://github.com/OAI/OpenAPI-Specification/issues/3148

It seems there is a slight preference for the version that django-ninja already has. Although type: [string, 'null'] is understood as a correct specification, it's maybe better that the Swift Generator gets support for anyOf rather than django-ninja changing?

Loeffeldude commented 1 month ago

You can patch the output schema by inheriting the NinjaAPI class and overriding the get_openapi_schema method like this:


from ninja.openapi.schema import OpenAPISchema
from typing import Any, Dict
from ninja import NinjaAPI

class AppNinjaAPI(NinjaAPI):
    def _fix_openapi_schema(self, schema: dict) -> OpenAPISchema:
        # we patch the schema here so anyOf gets turned into type: [type1, type2]
        for key, value in list(schema.items()):
            if key == "anyOf":
                schema["type"] = [item["type"] for item in value]
                del schema["anyOf"]
            elif isinstance(value, dict):
                schema[key] = self._fix_openapi_schema(value)

        return schema

    def get_openapi_schema(
        self,
        *,
        path_prefix: str | None = None,
        path_params: Dict[str, Any] | None = None,
    ) -> OpenAPISchema:
        schema = super().get_openapi_schema(
            path_prefix=path_prefix, path_params=path_params
        )

        self._fix_openapi_schema(schema)

        return schema