Example #1
0
 def _try_get_schema_for_enum(self, object_type: Type) -> Optional[Schema]:
     if issubclass(object_type, IntEnum):
         return Schema(type=ValueType.INTEGER,
                       enum=[v.value for v in object_type])
     if issubclass(object_type, Enum):
         return Schema(type=ValueType.STRING,
                       enum=[v.value for v in object_type])
     return None
Example #2
0
    def _get_schema_by_type(
            self, object_type: Type[Any]) -> Union[Schema, Reference]:
        # check_union
        if is_dataclass(object_type):
            return self._get_shema_for_dataclass(object_type)

        if (BaseModel is not ... and inspect.isclass(object_type)
                and issubclass(object_type, BaseModel)  # type: ignore
            ):
            return self._get_schema_for_pydantic_model(object_type)

        if isinstance(object_type, ForwardRef):
            # Note: this code does not support different classes with the same name
            # but defined in different modules as contracts of the API

            # Note: this is not supported in Swagger UI
            # https://github.com/swagger-api/swagger-ui/issues/3325
            ref_name = object_type.__forward_arg__  # type: ignore
            return Reference(f"#/components/schemas/{ref_name}")

        schema = self._try_get_schema_for_simple_type(object_type)
        if schema:
            return schema

        schema = self._try_get_schema_for_iterable(object_type)
        if schema:
            return schema

        if inspect.isclass(object_type):
            schema = self._try_get_schema_for_enum(object_type)
        return schema or Schema()
Example #3
0
    def _get_schema_for_pydantic_model(self, object_type: Type) -> Reference:
        assert BaseModel is not ..., "pydantic must be installed to use this method"
        assert issubclass(object_type, BaseModel)  # type: ignore
        assert hasattr(object_type, "__fields__")

        if object_type in self._objects_references:
            return self._objects_references[object_type]

        required: List[str] = []
        properties: Dict[str, Union[Schema, Reference]] = {}
        fields = object_type.__fields__  # type: ignore

        for field in fields.values():
            is_optional, child_type = check_union(field.type_)
            if not is_optional:
                required.append(field.name)
            properties[field.name] = self.get_schema_by_type(child_type)

        reference = self._register_schema(
            self._handle_subclasses(
                Schema(
                    type=ValueType.OBJECT,
                    required=required or None,
                    properties=properties,
                ),
                object_type,
            ),
            object_type.__name__,
        )
        self._objects_references[object_type] = reference
        return reference
Example #4
0
    def _get_shema_for_dataclass(self, object_type: Type) -> Reference:
        assert is_dataclass(object_type)

        if object_type in self._objects_references:
            return self._objects_references[object_type]

        required: List[str] = []
        properties: Dict[str, Union[Schema, Reference]] = {}

        # handle optional
        for field in fields(object_type):
            is_optional, child_type = check_union(field.type)
            if not is_optional:
                required.append(field.name)

            if isinstance(child_type, str):
                # this is a forward reference
                child_type = child_type.strip("'")
                properties[field.name] = Reference(f"#/components/schemas/{child_type}")
            else:
                properties[field.name] = self.get_schema_by_type(child_type)

        reference = self._register_schema(
            self._handle_subclasses(
                Schema(
                    type=ValueType.OBJECT,
                    required=required or None,
                    properties=properties,
                ),
                object_type,
            ),
            object_type.__name__,
        )
        self._objects_references[object_type] = reference
        return reference
Example #5
0
    def _try_get_schema_for_simple_type(self,
                                        object_type: Type) -> Optional[Schema]:
        if object_type is str:
            return Schema(type=ValueType.STRING)

        if object_type is int:
            # TODO: support control over format
            return Schema(type=ValueType.INTEGER, format=ValueFormat.INT64)

        if object_type is float:
            return Schema(type=ValueType.NUMBER, format=ValueFormat.FLOAT)

        if object_type is bool:
            return Schema(type=ValueType.BOOLEAN)

        if object_type is UUID:
            return Schema(type=ValueType.STRING, format=ValueFormat.UUID)

        if object_type is date:
            return Schema(type=ValueType.STRING, format=ValueFormat.DATE)

        if object_type is datetime:
            return Schema(type=ValueType.STRING, format=ValueFormat.DATETIME)

        return None
Example #6
0
    def _try_get_schema_for_iterable(self, object_type: Type) -> Optional[Schema]:
        if object_type in {list, set, tuple}:
            # the user didn't specify the item type
            return Schema(type=ValueType.ARRAY, items=Schema(type=ValueType.STRING))

        try:
            object_type.__origin__  # type: ignore
        except AttributeError:
            pass
        else:
            # can be List, List[str] or list[str] (Python 3.9),
            # note: it could also be union if it wasn't handled above for dataclasses
            try:
                type_args = object_type.__args__  # type: ignore
            except AttributeError:  # pragma: no cover
                item_type = str
            else:
                item_type = next(iter(type_args), str)

            return Schema(
                type=ValueType.ARRAY, items=self.get_schema_by_type(item_type)
            )
        return None
Example #7
0
def on_polymorph_example_docs_created_pydantic(
    docs: OpenAPIHandler, operation: Operation
) -> None:
    docs.register_schema_for_type(PetModel)
    pet_schema = docs.components.schemas["Pet"]
    assert isinstance(pet_schema, Schema)
    pet_schema.discriminator = Discriminator("type", {"cat": "CatPet", "dog": "DogPet"})

    cat_ref = docs.register_schema_for_type(CatPetModel)
    dog_ref = docs.register_schema_for_type(DogPetModel)

    operation.responses["200"] = ResponseDoc(
        "Polymorph example",
        content={
            "application/json": MediaType(schema=Schema(any_of=[cat_ref, dog_ref]))
        },
    )
Example #8
0
    def _handle_subclasses(self, schema: Schema, object_type: Type) -> Schema:
        """
        Method that implements automatic support for subclasses, handling Schema.allOf
        """
        assert inspect.isclass(object_type)
        direct_parent = object_type.__mro__[1]

        if direct_parent is object:
            return schema

        direct_parent_ref = self.register_schema_for_type(direct_parent)

        for inherited_class in object_type.__mro__[2:]:
            if inherited_class is object or not self._is_handled_object_type(
                    inherited_class):
                continue
            self.register_schema_for_type(inherited_class)
        return Schema(all_of=[direct_parent_ref, schema])