Esempio n. 1
0
def get_openapi_operation_request_body(
    *,
    body_field: Optional[ModelField],
    model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str],
) -> Optional[Dict]:
    if not body_field:
        return None
    assert isinstance(body_field, ModelField)
    # ignore mypy error until enum schemas are released
    body_schema, _, _ = field_schema(
        body_field, model_name_map=model_name_map, ref_prefix=REF_PREFIX  # type: ignore
    )
    field_info = cast(Body, body_field.field_info)
    request_media_type = field_info.media_type
    required = body_field.required
    request_body_oai: Dict[str, Any] = {}
    if required:
        request_body_oai["required"] = required
    request_body_oai["content"] = {request_media_type: {"schema": body_schema}}
    return request_body_oai
Esempio n. 2
0
def get_openapi_operation_request_body(
        *, body_field: Field, model_name_map: Dict[Type,
                                                   str]) -> Optional[Dict]:
    if not body_field:
        return None
    assert isinstance(body_field, Field)
    body_schema, _ = field_schema(body_field,
                                  model_name_map=model_name_map,
                                  ref_prefix=REF_PREFIX)
    schema: Schema = body_field.schema
    if isinstance(schema, Body):
        request_media_type = schema.media_type
    else:
        # Includes not declared media types (Schema)
        request_media_type = "application/json"
    required = body_field.required
    request_body_oai: Dict[str, Any] = {}
    if required:
        request_body_oai["required"] = required
    request_body_oai["content"] = {request_media_type: {"schema": body_schema}}
    return request_body_oai
Esempio n. 3
0
def get_openapi_operation_parameters(
    all_route_params: Sequence[Field]
) -> Tuple[Dict[str, Dict], List[Dict[str, Any]]]:
    definitions: Dict[str, Dict] = {}
    parameters = []
    for param in all_route_params:
        schema: Param = param.schema
        if "ValidationError" not in definitions:
            definitions["ValidationError"] = validation_error_definition
            definitions["HTTPValidationError"] = validation_error_response_definition
        parameter = {
            "name": param.alias,
            "in": schema.in_.value,
            "required": param.required,
            "schema": field_schema(param, model_name_map={})[0],
        }
        if schema.description:
            parameter["description"] = schema.description
        if schema.deprecated:
            parameter["deprecated"] = schema.deprecated
        parameters.append(parameter)
    return definitions, parameters
def get_openapi_operation_request_body(
    *,
    body_field: Optional[ModelField],
    model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str],
) -> Optional[Dict[str, Any]]:
    if not body_field:
        return None
    assert isinstance(body_field, ModelField)
    body_schema, _, _ = field_schema(
        body_field, model_name_map=model_name_map, ref_prefix=REF_PREFIX
    )
    field_info = cast(Body, body_field.field_info)
    request_media_type = field_info.media_type
    required = body_field.required
    request_body_oai: Dict[str, Any] = {}
    if required:
        request_body_oai["required"] = required
    request_media_content: Dict[str, Any] = {"schema": body_schema}
    if field_info.examples:
        request_media_content["examples"] = jsonable_encoder(field_info.examples)
    elif field_info.example != Undefined:
        request_media_content["example"] = jsonable_encoder(field_info.example)
    request_body_oai["content"] = {request_media_type: request_media_content}
    return request_body_oai
Esempio n. 5
0
def get_openapi_operation_parameters(
    *,
    all_route_params: Sequence[ModelField],
    model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str],
) -> List[Dict[str, Any]]:
    parameters = []
    for param in all_route_params:
        field_info = param.field_info
        field_info = cast(Param, field_info)
        # ignore mypy error until enum schemas are released
        parameter = {
            "name": param.alias,
            "in": field_info.in_.value,
            "required": param.required,
            "schema": field_schema(
                param, model_name_map=model_name_map, ref_prefix=REF_PREFIX  # type: ignore
            )[0],
        }
        if field_info.description:
            parameter["description"] = field_info.description
        if field_info.deprecated:
            parameter["deprecated"] = field_info.deprecated
        parameters.append(parameter)
    return parameters
Esempio n. 6
0
def get_openapi_path(
        *, route: routing.APIRoute,
        model_name_map: Dict[Type, str]) -> Tuple[Dict, Dict, Dict]:
    path = {}
    security_schemes: Dict[str, Any] = {}
    definitions: Dict[str, Any] = {}
    assert route.methods is not None, "Methods must be a list"
    if route.include_in_schema:
        for method in route.methods:
            operation = get_openapi_operation_metadata(route=route,
                                                       method=method)
            parameters: List[Dict] = []
            flat_dependant = get_flat_dependant(route.dependant)
            security_definitions, operation_security = get_openapi_security_definitions(
                flat_dependant=flat_dependant)
            if operation_security:
                operation.setdefault("security", []).extend(operation_security)
            if security_definitions:
                security_schemes.update(security_definitions)
            all_route_params = get_openapi_params(route.dependant)
            validation_definitions, operation_parameters = get_openapi_operation_parameters(
                all_route_params=all_route_params)
            definitions.update(validation_definitions)
            parameters.extend(operation_parameters)
            if parameters:
                operation["parameters"] = parameters
            if method in METHODS_WITH_BODY:
                request_body_oai = get_openapi_operation_request_body(
                    body_field=route.body_field, model_name_map=model_name_map)
                if request_body_oai:
                    operation["requestBody"] = request_body_oai
                    if "ValidationError" not in definitions:
                        definitions[
                            "ValidationError"] = validation_error_definition
                        definitions[
                            "HTTPValidationError"] = validation_error_response_definition
            if route.responses:
                for (additional_status_code,
                     response) in route.responses.items():
                    assert isinstance(
                        response,
                        dict), "An additional response must be a dict"
                    field = route.response_fields.get(additional_status_code)
                    if field:
                        response_schema, _ = field_schema(
                            field,
                            model_name_map=model_name_map,
                            ref_prefix=REF_PREFIX)
                        response.setdefault("content", {}).setdefault(
                            "application/json", {})["schema"] = response_schema
                    response.setdefault("description", "Additional Response")
                    operation.setdefault(
                        "responses",
                        {})[str(additional_status_code)] = response
            status_code = str(route.status_code)
            response_schema = {"type": "string"}
            if lenient_issubclass(route.content_type, JSONResponse):
                if route.response_field:
                    response_schema, _ = field_schema(
                        route.response_field,
                        model_name_map=model_name_map,
                        ref_prefix=REF_PREFIX,
                    )
                else:
                    response_schema = {}
            operation.setdefault("responses", {}).setdefault(
                status_code, {})["description"] = route.response_description
            operation.setdefault("responses", {}).setdefault(
                status_code,
                {}).setdefault("content",
                               {}).setdefault(route.content_type.media_type,
                                              {})["schema"] = response_schema
            if all_route_params or route.body_field:
                operation["responses"][str(HTTP_422_UNPROCESSABLE_ENTITY)] = {
                    "description": "Validation Error",
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": REF_PREFIX + "HTTPValidationError"
                            }
                        }
                    },
                }
            path[method.lower()] = operation
    return path, security_schemes, definitions
Esempio n. 7
0
def get_openapi_path(
    *, route: routing.APIRoute, model_name_map: Dict[Type, str]
) -> Tuple[Dict, Dict, Dict]:
    path = {}
    security_schemes: Dict[str, Any] = {}
    definitions: Dict[str, Any] = {}
    assert route.methods is not None, "Methods must be a list"
    assert route.response_class, "A response class is needed to generate OpenAPI"
    route_response_media_type: Optional[str] = route.response_class.media_type
    if route.include_in_schema:
        for method in route.methods:
            operation = get_openapi_operation_metadata(route=route, method=method)
            parameters: List[Dict] = []
            flat_dependant = get_flat_dependant(route.dependant, skip_repeats=True)
            security_definitions, operation_security = get_openapi_security_definitions(
                flat_dependant=flat_dependant
            )
            if operation_security:
                operation.setdefault("security", []).extend(operation_security)
            if security_definitions:
                security_schemes.update(security_definitions)
            all_route_params = get_openapi_params(route.dependant)
            operation_parameters = get_openapi_operation_parameters(all_route_params)
            parameters.extend(operation_parameters)
            if parameters:
                operation["parameters"] = parameters
            if method in METHODS_WITH_BODY:
                request_body_oai = get_openapi_operation_request_body(
                    body_field=route.body_field, model_name_map=model_name_map
                )
                if request_body_oai:
                    operation["requestBody"] = request_body_oai
            if route.callbacks:
                callbacks = {}
                for callback in route.callbacks:
                    cb_path, cb_security_schemes, cb_definitions, = get_openapi_path(
                        route=callback, model_name_map=model_name_map
                    )
                    callbacks[callback.name] = {callback.path: cb_path}
                operation["callbacks"] = callbacks
            if route.responses:
                for (additional_status_code, response) in route.responses.items():
                    assert isinstance(
                        response, dict
                    ), "An additional response must be a dict"
                    field = route.response_fields.get(additional_status_code)
                    if field:
                        response_schema, _, _ = field_schema(
                            field, model_name_map=model_name_map, ref_prefix=REF_PREFIX
                        )
                        response.setdefault("content", {}).setdefault(
                            route_response_media_type or "application/json", {}
                        )["schema"] = response_schema
                    status_text: Optional[str] = status_code_ranges.get(
                        str(additional_status_code).upper()
                    ) or http.client.responses.get(int(additional_status_code))
                    response.setdefault(
                        "description", status_text or "Additional Response"
                    )
                    status_code_key = str(additional_status_code).upper()
                    if status_code_key == "DEFAULT":
                        status_code_key = "default"
                    operation.setdefault("responses", {})[status_code_key] = response
            status_code = str(route.status_code)
            operation.setdefault("responses", {}).setdefault(status_code, {})[
                "description"
            ] = route.response_description
            if (
                route_response_media_type
                and route.status_code not in STATUS_CODES_WITH_NO_BODY
            ):
                response_schema = {"type": "string"}
                if lenient_issubclass(route.response_class, JSONResponse):
                    if route.response_field:
                        response_schema, _, _ = field_schema(
                            route.response_field,
                            model_name_map=model_name_map,
                            ref_prefix=REF_PREFIX,
                        )
                    else:
                        response_schema = {}
                operation.setdefault("responses", {}).setdefault(
                    status_code, {}
                ).setdefault("content", {}).setdefault(route_response_media_type, {})[
                    "schema"
                ] = response_schema

            http422 = str(HTTP_422_UNPROCESSABLE_ENTITY)
            if (all_route_params or route.body_field) and not any(
                [
                    status in operation["responses"]
                    for status in [http422, "4XX", "default"]
                ]
            ):
                operation["responses"][http422] = {
                    "description": "Validation Error",
                    "content": {
                        "application/json": {
                            "schema": {"$ref": REF_PREFIX + "HTTPValidationError"}
                        }
                    },
                }
                if "ValidationError" not in definitions:
                    definitions.update(
                        {
                            "ValidationError": validation_error_definition,
                            "HTTPValidationError": validation_error_response_definition,
                        }
                    )
            path[method.lower()] = operation
    return path, security_schemes, definitions
Esempio n. 8
0
from pydantic import BaseModel
from pydantic.fields import ModelField
from pydantic.schema import schema, field_schema


class Message(BaseModel):
    message: str


class Wrap(BaseModel):
    message: Message


print(schema([Message]))
print(field_schema(Message.__fields__["message"], model_name_map={}))

print(schema([Wrap]))
print(Wrap.__fields__)
print(
    field_schema(Wrap.__fields__["message"],
                 model_name_map={Message: "Message"}))
Esempio n. 9
0
def get_openapi_path(
    *, route: routing.APIRoute, model_name_map: Dict[type, str],
    operation_ids: Set[str]
) -> Tuple[Dict[str, Any], Dict[str, Any], Dict[str, Any]]:
    path = {}
    security_schemes: Dict[str, Any] = {}
    definitions: Dict[str, Any] = {}
    assert route.methods is not None, "Methods must be a list"
    if isinstance(route.response_class, DefaultPlaceholder):
        current_response_class: Type[Response] = route.response_class.value
    else:
        current_response_class = route.response_class
    assert current_response_class, "A response class is needed to generate OpenAPI"
    route_response_media_type: Optional[
        str] = current_response_class.media_type
    if route.include_in_schema:
        for method in route.methods:
            operation = get_openapi_operation_metadata(
                route=route, method=method, operation_ids=operation_ids)
            parameters: List[Dict[str, Any]] = []
            flat_dependant = get_flat_dependant(route.dependant,
                                                skip_repeats=True)
            security_definitions, operation_security = get_openapi_security_definitions(
                flat_dependant=flat_dependant)
            if operation_security:
                operation.setdefault("security", []).extend(operation_security)
            if security_definitions:
                security_schemes.update(security_definitions)
            all_route_params = get_flat_params(route.dependant)
            operation_parameters = get_openapi_operation_parameters(
                all_route_params=all_route_params,
                model_name_map=model_name_map)
            parameters.extend(operation_parameters)
            if parameters:
                operation["parameters"] = list(
                    {param["name"]: param
                     for param in parameters}.values())
            if method in METHODS_WITH_BODY:
                request_body_oai = get_openapi_operation_request_body(
                    body_field=route.body_field, model_name_map=model_name_map)
                if request_body_oai:
                    operation["requestBody"] = request_body_oai
            if route.callbacks:
                callbacks = {}
                for callback in route.callbacks:
                    if isinstance(callback, routing.APIRoute):
                        (
                            cb_path,
                            cb_security_schemes,
                            cb_definitions,
                        ) = get_openapi_path(
                            route=callback,
                            model_name_map=model_name_map,
                            operation_ids=operation_ids,
                        )
                        callbacks[callback.name] = {callback.path: cb_path}
                operation["callbacks"] = callbacks
            if route.status_code is not None:
                status_code = str(route.status_code)
            else:
                # It would probably make more sense for all response classes to have an
                # explicit default status_code, and to extract it from them, instead of
                # doing this inspection tricks, that would probably be in the future
                # TODO: probably make status_code a default class attribute for all
                # responses in Starlette
                response_signature = inspect.signature(
                    current_response_class.__init__)
                status_code_param = response_signature.parameters.get(
                    "status_code")
                if status_code_param is not None:
                    if isinstance(status_code_param.default, int):
                        status_code = str(status_code_param.default)
            operation.setdefault("responses", {}).setdefault(
                status_code, {})["description"] = route.response_description
            if (route_response_media_type
                    and route.status_code not in STATUS_CODES_WITH_NO_BODY):
                response_schema = {"type": "string"}
                if lenient_issubclass(current_response_class, JSONResponse):
                    if route.response_field:
                        response_schema, _, _ = field_schema(
                            route.response_field,
                            model_name_map=model_name_map,
                            ref_prefix=REF_PREFIX,
                        )
                    else:
                        response_schema = {}
                operation.setdefault("responses", {}).setdefault(
                    status_code, {}).setdefault("content", {}).setdefault(
                        route_response_media_type,
                        {})["schema"] = response_schema
            if route.responses:
                operation_responses = operation.setdefault("responses", {})
                for (
                        additional_status_code,
                        additional_response,
                ) in route.responses.items():
                    process_response = additional_response.copy()
                    process_response.pop("model", None)
                    status_code_key = str(additional_status_code).upper()
                    if status_code_key == "DEFAULT":
                        status_code_key = "default"
                    openapi_response = operation_responses.setdefault(
                        status_code_key, {})
                    assert isinstance(
                        process_response,
                        dict), "An additional response must be a dict"
                    field = route.response_fields.get(additional_status_code)
                    additional_field_schema: Optional[Dict[str, Any]] = None
                    if field:
                        additional_field_schema, _, _ = field_schema(
                            field,
                            model_name_map=model_name_map,
                            ref_prefix=REF_PREFIX)
                        media_type = route_response_media_type or "application/json"
                        additional_schema = (process_response.setdefault(
                            "content",
                            {}).setdefault(media_type,
                                           {}).setdefault("schema", {}))
                        deep_dict_update(additional_schema,
                                         additional_field_schema)
                    status_text: Optional[str] = status_code_ranges.get(
                        str(additional_status_code).upper()
                    ) or http.client.responses.get(int(additional_status_code))
                    description = (process_response.get("description")
                                   or openapi_response.get("description")
                                   or status_text or "Additional Response")
                    deep_dict_update(openapi_response, process_response)
                    openapi_response["description"] = description
            http422 = str(HTTP_422_UNPROCESSABLE_ENTITY)
            if (all_route_params or route.body_field) and not any([
                    status in operation["responses"]
                    for status in [http422, "4XX", "default"]
            ]):
                operation["responses"][http422] = {
                    "description": "Validation Error",
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": REF_PREFIX + "HTTPValidationError"
                            }
                        }
                    },
                }
                if "ValidationError" not in definitions:
                    definitions.update({
                        "ValidationError":
                        validation_error_definition,
                        "HTTPValidationError":
                        validation_error_response_definition,
                    })
            if route.openapi_extra:
                deep_dict_update(operation, route.openapi_extra)
            path[method.lower()] = operation
    return path, security_schemes, definitions
Esempio n. 10
0
def get_openapi_path(
    *, route: ViewRoute, model_name_map: Dict[Type, str]
) -> Tuple[Dict, Dict]:
    path = {}
    definitions: Dict[str, Any] = {}
    assert route.methods is not None, "Methods must be a list"
    assert route.response_class, "A response class is n" "eeded to generate OpenAPI"
    #
    route_response_media_type = "application/json"
    if route.include_in_schema:
        for method in route.methods:
            operation = get_openapi_operation_metadata(route=route, method=method)
            parameters: List[Dict] = []
            all_route_params = route.dependant.get_flat_params()
            operation_parameters = get_openapi_operation_parameters(
                all_route_params=all_route_params, model_name_map=model_name_map
            )
            parameters.extend(operation_parameters)
            if parameters:
                operation["parameters"] = list(
                    {param["name"]: param for param in parameters}.values()
                )
            if method in METHODS_WITH_BODY:
                request_body_oai = get_openapi_operation_request_body(
                    body_field=route.body_field, model_name_map=model_name_map
                )
                if request_body_oai:
                    operation["requestBody"] = request_body_oai
            status_code = str(route.status_code)
            operation.setdefault("responses", {}).setdefault(status_code, {})[
                "description"
            ] = route.response_description
            if (
                # route_response_media_type
                route.status_code
                not in STATUS_CODES_WITH_NO_BODY
            ):
                response_schema = {"type": "string"}
                if lenient_issubclass(route.response_class, JsonResponse):
                    if route.response_field:
                        response_schema, _, _ = field_schema(
                            route.response_field,
                            model_name_map=model_name_map,
                            ref_prefix=REF_PREFIX,
                        )
                    else:
                        response_schema = {}
                operation.setdefault("responses", {}).setdefault(
                    status_code, {}
                ).setdefault("content", {}).setdefault(route_response_media_type, {})[
                    "schema"
                ] = response_schema
            http422 = str(HTTP_422_UNPROCESSABLE_ENTITY)
            #
            if (all_route_params or route.body_field) and not any(
                [
                    status in operation["responses"]
                    for status in [http422, "4XX", "default"]
                ]
            ):
                operation["responses"][http422] = {
                    "description": "Validation Error",
                    "content": {
                        "application/json": {
                            "schema": {"$ref": REF_PREFIX + "HTTPValidationError"}
                        }
                    },
                }
                if "ValidationError" not in definitions:
                    definitions.update(
                        {
                            "ValidationError": validation_error_definition,
                            "HTTPValidationError": validation_error_response_definition,
                        }
                    )
            path[method.lower()] = operation

    return path, definitions