コード例 #1
0
def get_flat_models_from_routes(
    routes: Sequence[BaseRoute], ) -> Set[Union[Type[BaseModel], Type[Enum]]]:
    body_fields_from_routes: List[ModelField] = []
    responses_from_routes: List[ModelField] = []
    request_fields_from_routes: List[ModelField] = []
    callback_flat_models: Set[Union[Type[BaseModel], Type[Enum]]] = set()
    for route in routes:
        if getattr(route, "include_in_schema", None) and isinstance(
                route, routing.APIRoute):
            if route.body_field:
                assert isinstance(
                    route.body_field,
                    ModelField), "A request body must be a Pydantic Field"
                body_fields_from_routes.append(route.body_field)
            if route.response_field:
                responses_from_routes.append(route.response_field)
            if route.response_fields:
                responses_from_routes.extend(route.response_fields.values())
            if route.callbacks:
                callback_flat_models |= get_flat_models_from_routes(
                    route.callbacks)
            params = get_flat_params(route.dependant)
            request_fields_from_routes.extend(params)

    flat_models = callback_flat_models | get_flat_models_from_fields(
        body_fields_from_routes + responses_from_routes +
        request_fields_from_routes,
        known_models=set(),
    )
    return flat_models
コード例 #2
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_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:
                    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():
                    process_response = response.copy()
                    assert isinstance(
                        process_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)
                        process_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))
                    process_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"
                    process_response.pop("model", None)
                    operation.setdefault(
                        "responses", {})[status_code_key] = process_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
コード例 #3
0
ファイル: utils.py プロジェクト: xaviml/fastapi
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