def start_hacking(prodigy_data_provider_by_doi): def hacky_get_questions(request: Request): referer = request.headers.get("referer") query = parse_qs(urlparse(referer).query) doi = query.get('doi', None) if isinstance(doi, list) and len(doi) == 1: doi = doi[0] prodigy_data = list(prodigy_data_provider_by_doi(doi)) result = { 'total': 1, 'progress': None, 'session_id': None, 'tasks': prodigy_data } return JSONResponse(result) elif not isinstance(doi, str): doi = 'No doi!' return JSONResponse(_shared_get_questions(None)) with open(os.path.join(os.path.dirname(__file__), 'bundle.js'), encoding='utf8') as f: my_js = f.read() with open(os.path.join(os.path.dirname(__file__), 'index.html'), encoding='utf8') as f: my_html = f.read() with open(os.path.join(os.path.dirname(__file__), 'custom_style.css'), encoding='utf8') as f: my_css = f.read() my_html = my_html.replace('<!--placeholder for custom_style.css-->', '\n<style>\n{}\n</style>\n'.format(my_css)) def static_bundle(_): return Response(my_js, media_type="application/javascript") def static_index(_): return Response(my_html, media_type="text/html") for i, route in enumerate(prodigy_app.router.routes): if route.path == '/get_session_questions': route.endpoint = hacky_get_questions route.app = request_response(hacky_get_questions) elif route.path == '/bundle.js': route.endpoint = static_bundle route.app = request_response(static_bundle) elif route.path in {'/', '/index.html'}: route.endpoint = static_index route.app = request_response(static_index)
def app_routes_add_auth(self, app: FastAPI, route_list: List[str], invert: bool = False): """Add authentication to specified routes in application router. Used for default routes (e.g. api/docs and api/redocs, openapi.json etc) Args: app: fastapi application route_list: list of routes to add authentication to (e.g. api docs, redocs etc) Keyword Args: invert: Switch between using the route list as a block list or an allow list """ if self.config.enabled: routes = app.router.routes for i, route in enumerate(routes): # Can use allow list or block list (i.e. invert = True sets all except the route list to have auth if (route.name in route_list and not invert) or (route.name not in route_list and invert): # type: ignore route.endpoint = self.auth_required()( route.endpoint) # type: ignore route.app = request_response( route.endpoint) # type: ignore app.router.routes[i] = route return app
def app_routes_add_auth(self, app, route_list, ignore=False): if self.oauth_backend.enabled: routes = app.router.routes for i, route in enumerate(routes): # Can use allow list or block list (i.e. ignore = True sets all except the route list to have auth if (route.name in route_list and not ignore) or (route.name not in route_list and ignore): route.endpoint = self.auth_required()(route.endpoint) route.app = request_response(route.endpoint) app.router.routes[i] = route return app
def __init__( self, path: str, endpoint: Callable, *, response_model: Type[Any] = None, status_code: int = 200, tags: List[str] = None, dependencies: Sequence[params.Depends] = None, summary: str = None, description: str = None, response_description: str = "Successful Response", responses: Dict[Union[int, str], Dict[str, Any]] = None, deprecated: bool = None, name: str = None, methods: Optional[Union[Set[str], List[str]]] = None, operation_id: str = None, response_model_include: Union[SetIntStr, DictIntStrAny] = None, response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(), response_model_by_alias: bool = True, response_model_exclude_unset: bool = False, include_in_schema: bool = True, response_class: Optional[Type[Response]] = None, dependency_overrides_provider: Any = None, callbacks: Optional[List["APIRoute"]] = None, ) -> None: self.path = path self.endpoint = endpoint self.name = get_name(endpoint) if name is None else name self.path_regex, self.path_format, self.param_convertors = compile_path(path) if methods is None: methods = ["GET"] self.methods = set([method.upper() for method in methods]) self.unique_id = generate_operation_id_for_path( name=self.name, path=self.path_format, method=list(methods)[0] ) self.response_model = response_model if self.response_model: assert ( status_code not in STATUS_CODES_WITH_NO_BODY ), f"Status code {status_code} must not have a response body" response_name = "Response_" + self.unique_id self.response_field = create_response_field( name=response_name, type_=self.response_model ) # Create a clone of the field, so that a Pydantic submodel is not returned # as is just because it's an instance of a subclass of a more limited class # e.g. UserInDB (containing hashed_password) could be a subclass of User # that doesn't have the hashed_password. But because it's a subclass, it # would pass the validation and be returned as is. # By being a new field, no inheritance will be passed as is. A new model # will be always created. self.secure_cloned_response_field: Optional[ ModelField ] = create_cloned_field(self.response_field) else: self.response_field = None # type: ignore self.secure_cloned_response_field = None self.status_code = status_code self.tags = tags or [] if dependencies: self.dependencies = list(dependencies) else: self.dependencies = [] self.summary = summary self.description = description or inspect.cleandoc(self.endpoint.__doc__ or "") # if a "form feed" character (page break) is found in the description text, # truncate description text to the content preceding the first "form feed" self.description = self.description.split("\f")[0] self.response_description = response_description self.responses = responses or {} response_fields = {} for additional_status_code, response in self.responses.items(): assert isinstance(response, dict), "An additional response must be a dict" model = response.get("model") if model: assert ( additional_status_code not in STATUS_CODES_WITH_NO_BODY ), f"Status code {additional_status_code} must not have a response body" response_name = f"Response_{additional_status_code}_{self.unique_id}" response_field = create_response_field(name=response_name, type_=model) response_fields[additional_status_code] = response_field if response_fields: self.response_fields: Dict[Union[int, str], ModelField] = response_fields else: self.response_fields = {} self.deprecated = deprecated self.operation_id = operation_id self.response_model_include = response_model_include self.response_model_exclude = response_model_exclude self.response_model_by_alias = response_model_by_alias self.response_model_exclude_unset = response_model_exclude_unset self.include_in_schema = include_in_schema self.response_class = response_class assert callable(endpoint), f"An endpoint must be a callable" self.dependant = get_dependant(path=self.path_format, call=self.endpoint) for depends in self.dependencies[::-1]: self.dependant.dependencies.insert( 0, get_parameterless_sub_dependant(depends=depends, path=self.path_format), ) self.body_field = get_body_field(dependant=self.dependant, name=self.unique_id) self.dependency_overrides_provider = dependency_overrides_provider self.callbacks = callbacks self.app = request_response(self.get_route_handler())
def __init__( self, path: str, endpoint: Callable, *, response_model: Type[BaseModel] = None, status_code: int = 200, tags: List[str] = None, summary: str = None, description: str = None, response_description: str = "Successful Response", responses: Dict[Union[int, str], Dict[str, Any]] = None, deprecated: bool = None, name: str = None, methods: List[str] = None, operation_id: str = None, include_in_schema: bool = True, response_class: Type[Response] = JSONResponse, ) -> None: assert path.startswith("/"), "Routed paths must always start with '/'" self.path = path self.endpoint = endpoint self.name = get_name(endpoint) if name is None else name self.response_model = response_model if self.response_model: assert lenient_issubclass( response_class, JSONResponse ), "To declare a type the response must be a JSON response" response_name = "Response_" + self.name self.response_field: Optional[Field] = Field( name=response_name, type_=self.response_model, class_validators={}, default=None, required=False, model_config=BaseConfig, schema=Schema(None), ) else: self.response_field = None self.status_code = status_code self.tags = tags or [] self.summary = summary self.description = description or inspect.cleandoc( self.endpoint.__doc__ or "") self.response_description = response_description self.responses = responses or {} response_fields = {} for additional_status_code, response in self.responses.items(): assert isinstance(response, dict), "An additional response must be a dict" model = response.get("model") if model: assert lenient_issubclass( model, BaseModel), "A response model must be a Pydantic model" response_name = f"Response_{additional_status_code}_{self.name}" response_field = Field( name=response_name, type_=model, class_validators=None, default=None, required=False, model_config=BaseConfig, schema=Schema(None), ) response_fields[additional_status_code] = response_field if response_fields: self.response_fields: Dict[Union[int, str], Field] = response_fields else: self.response_fields = {} self.deprecated = deprecated if methods is None: methods = ["GET"] self.methods = methods self.operation_id = operation_id self.include_in_schema = include_in_schema self.response_class = response_class self.path_regex, self.path_format, self.param_convertors = compile_path( path) assert inspect.isfunction(endpoint) or inspect.ismethod( endpoint), f"An endpoint must be a function or method" self.dependant = get_dependant(path=path, call=self.endpoint) self.body_field = get_body_field(dependant=self.dependant, name=self.name) self.app = request_response( get_app( dependant=self.dependant, body_field=self.body_field, status_code=self.status_code, response_class=self.response_class, response_field=self.response_field, ))
def __init__( self, path: str, endpoint: Callable, *, response_model: Type[Any] = None, status_code: int = 200, tags: List[str] = None, dependencies: Sequence[params.Depends] = None, summary: str = None, description: str = None, response_description: str = "Successful Response", responses: Dict[Union[int, str], Dict[str, Any]] = None, deprecated: bool = None, name: str = None, methods: Optional[Union[Set[str], List[str]]] = None, operation_id: str = None, response_model_include: Set[str] = None, response_model_exclude: Set[str] = set(), response_model_by_alias: bool = True, response_model_skip_defaults: bool = False, include_in_schema: bool = True, response_class: Type[Response] = JSONResponse, dependency_overrides_provider: Any = None, ) -> None: assert path.startswith("/"), "Routed paths must always start with '/'" self.path = path self.endpoint = endpoint self.name = get_name(endpoint) if name is None else name self.path_regex, self.path_format, self.param_convertors = compile_path( path) if methods is None: methods = ["GET"] self.methods = set([method.upper() for method in methods]) self.unique_id = generate_operation_id_for_path( name=self.name, path=self.path_format, method=list(methods)[0]) self.response_model = response_model if self.response_model: assert lenient_issubclass( response_class, JSONResponse ), "To declare a type the response must be a JSON response" response_name = "Response_" + self.unique_id self.response_field: Optional[Field] = Field( name=response_name, type_=self.response_model, class_validators={}, default=None, required=False, model_config=BaseConfig, schema=Schema(None), ) # Create a clone of the field, so that a Pydantic submodel is not returned # as is just because it's an instance of a subclass of a more limited class # e.g. UserInDB (containing hashed_password) could be a subclass of User # that doesn't have the hashed_password. But because it's a subclass, it # would pass the validation and be returned as is. # By being a new field, no inheritance will be passed as is. A new model # will be always created. self.secure_cloned_response_field: Optional[ Field] = create_cloned_field(self.response_field) else: self.response_field = None self.secure_cloned_response_field = None self.status_code = status_code self.tags = tags or [] if dependencies: self.dependencies = list(dependencies) else: self.dependencies = [] self.summary = summary self.description = description or inspect.cleandoc( self.endpoint.__doc__ or "") self.response_description = response_description self.responses = responses or {} response_fields = {} for additional_status_code, response in self.responses.items(): assert isinstance(response, dict), "An additional response must be a dict" model = response.get("model") if model: assert lenient_issubclass( model, BaseModel), "A response model must be a Pydantic model" response_name = f"Response_{additional_status_code}_{self.unique_id}" response_field = Field( name=response_name, type_=model, class_validators=None, default=None, required=False, model_config=BaseConfig, schema=Schema(None), ) response_fields[additional_status_code] = response_field if response_fields: self.response_fields: Dict[Union[int, str], Field] = response_fields else: self.response_fields = {} self.deprecated = deprecated self.operation_id = operation_id self.response_model_include = response_model_include self.response_model_exclude = response_model_exclude self.response_model_by_alias = response_model_by_alias self.response_model_skip_defaults = response_model_skip_defaults self.include_in_schema = include_in_schema self.response_class = response_class assert inspect.isfunction(endpoint) or inspect.ismethod( endpoint), f"An endpoint must be a function or method" self.dependant = get_dependant(path=self.path_format, call=self.endpoint) for depends in self.dependencies[::-1]: self.dependant.dependencies.insert( 0, get_parameterless_sub_dependant(depends=depends, path=self.path_format), ) self.body_field = get_body_field(dependant=self.dependant, name=self.unique_id) self.dependency_overrides_provider = dependency_overrides_provider self.app = request_response( get_app( dependant=self.dependant, body_field=self.body_field, status_code=self.status_code, response_class=self.response_class, response_field=self.secure_cloned_response_field, response_model_include=self.response_model_include, response_model_exclude=self.response_model_exclude, response_model_by_alias=self.response_model_by_alias, response_model_skip_defaults=self.response_model_skip_defaults, dependency_overrides_provider=self. dependency_overrides_provider, ))
def __init__( self, path: str, endpoint: Callable, *, response_model: Type[BaseModel] = None, status_code: int = 200, tags: List[str] = None, summary: str = None, description: str = None, response_description: str = "Successful Response", deprecated: bool = None, name: str = None, methods: List[str] = None, operation_id: str = None, include_in_schema: bool = True, content_type: Type[Response] = JSONResponse, ) -> None: assert path.startswith("/"), "Routed paths must always start with '/'" self.path = path self.endpoint = endpoint self.name = get_name(endpoint) if name is None else name self.response_model = response_model if self.response_model: assert lenient_issubclass( content_type, JSONResponse ), "To declare a type the response must be a JSON response" response_name = "Response_" + self.name self.response_field: Optional[Field] = Field( name=response_name, type_=self.response_model, class_validators=[], default=None, required=False, model_config=UnconstrainedConfig, schema=Schema(None), ) else: self.response_field = None self.status_code = status_code self.tags = tags or [] self.summary = summary self.description = description or self.endpoint.__doc__ self.response_description = response_description self.deprecated = deprecated if methods is None: methods = ["GET"] self.methods = methods self.operation_id = operation_id self.include_in_schema = include_in_schema self.content_type = content_type self.path_regex, self.path_format, self.param_convertors = self.compile_path( path) assert inspect.isfunction(endpoint) or inspect.ismethod( endpoint), f"An endpoint must be a function or method" self.dependant = get_dependant(path=path, call=self.endpoint) self.body_field = get_body_field(dependant=self.dependant, name=self.name) self.app = request_response( get_app( dependant=self.dependant, body_field=self.body_field, status_code=self.status_code, content_type=self.content_type, response_field=self.response_field, ))
def __init__( self, entrypoint: 'Entrypoint', path: str, *, name: str = None, errors: Sequence[Type[BaseError]] = None, common_dependencies: Sequence[Depends] = None, response_class: Type[Response] = JSONResponse, **kwargs, ): name = name or 'entrypoint' _, path_format, _ = compile_path(path) _Request = JsonRpcRequest common_dependant = Dependant(path=path_format) if common_dependencies: insert_dependencies(common_dependant, common_dependencies) fix_query_dependencies(common_dependant) common_dependant = get_flat_dependant(common_dependant, skip_repeats=True) if common_dependant.body_params: _Request = make_request_model(name, entrypoint.callee_module, common_dependant.body_params) # This is only necessary for generating OpenAPI def endpoint(__request__: _Request): del __request__ responses = errors_responses(errors) super().__init__( path, endpoint, methods=['POST'], name=name, response_class=response_class, response_model=JsonRpcResponse, responses=responses, **kwargs, ) flat_dependant = get_flat_dependant(self.dependant, skip_repeats=True) if len(flat_dependant.body_params) > 1: body_params = [p for p in flat_dependant.body_params if p.type_ is not _Request] raise RuntimeError( f"Entrypoint shared dependencies can't use 'Body' parameters: " f"params={body_params}" ) if flat_dependant.query_params: raise RuntimeError( f"Entrypoint shared dependencies can't use 'Query' parameters: " f"params={flat_dependant.query_params}" ) self.shared_dependant = clone_dependant(self.dependant) # No shared 'Body' params, because each JSON-RPC request in batch has own body self.shared_dependant.body_params = [] # Add dependencies and other parameters from common_dependant for correct OpenAPI generation self.dependant.path_params.extend(common_dependant.path_params) self.dependant.header_params.extend(common_dependant.header_params) self.dependant.cookie_params.extend(common_dependant.cookie_params) self.dependant.dependencies.extend(common_dependant.dependencies) self.dependant.security_requirements.extend(common_dependant.security_requirements) self.app = request_response(self.handle_http_request) self.entrypoint = entrypoint self.common_dependencies = common_dependencies
def __init__( self, entrypoint: 'Entrypoint', path: str, func: Union[FunctionType, CoroutineType], *, result_model: Type[Any] = None, name: str = None, errors: Sequence[Type[BaseError]] = None, dependencies: Sequence[Depends] = None, response_class: Type[Response] = JSONResponse, **kwargs, ): name = name or func.__name__ result_model = result_model or func.__annotations__.get('return') _, path_format, _ = compile_path(path) func_dependant = get_dependant(path=path_format, call=func) insert_dependencies(func_dependant, dependencies) insert_dependencies(func_dependant, entrypoint.common_dependencies) fix_query_dependencies(func_dependant) flat_dependant = get_flat_dependant(func_dependant, skip_repeats=True) _Request = make_request_model(name, func.__module__, flat_dependant.body_params) @component_name(f'_Response[{name}]', func.__module__) class _Response(BaseModel): jsonrpc: StrictStr = Field('2.0', const=True, example='2.0') id: Union[StrictStr, int] = Field(None, example=0) result: result_model class Config: extra = 'forbid' # Only needed to generate OpenAPI async def endpoint(__request__: _Request): del __request__ endpoint.__name__ = func.__name__ endpoint.__doc__ = func.__doc__ responses = errors_responses(errors) super().__init__( path, endpoint, methods=['POST'], name=name, response_class=response_class, response_model=_Response, response_model_exclude_unset=True, responses=responses, **kwargs, ) # Add dependencies and other parameters from func_dependant for correct OpenAPI generation self.dependant.path_params = func_dependant.path_params self.dependant.header_params = func_dependant.header_params self.dependant.cookie_params = func_dependant.cookie_params self.dependant.dependencies = func_dependant.dependencies self.dependant.security_requirements = func_dependant.security_requirements self.func = func self.func_dependant = func_dependant self.entrypoint = entrypoint self.app = request_response(self.handle_http_request)
from starlette.requests import Request from starlette.responses import JSONResponse, PlainTextResponse, Response from starlette.routing import Mount, Route, request_response from asgi_caches.decorators import cache_control from asgi_caches.middleware import CacheMiddleware from tests.utils import CacheSpy from .resources import cache, special_cache async def _home(request: Request) -> Response: return PlainTextResponse("Hello, world!") home = CacheSpy(request_response(_home)) @cache_control(max_age=30, must_revalidate=True) class Pi(HTTPEndpoint): async def get(self, request: Request) -> Response: return JSONResponse({"value": math.pi}) pi = CacheSpy(Pi) class Exp(HTTPEndpoint): async def get(self, request: Request) -> Response: return JSONResponse({"value": math.e})
return prodigy_data def hacky_get_questions(request: Request): referer = request.headers.get("referer") query = parse_qs(urlparse(referer).query) doi = query.get('doi', None) if isinstance(doi, list) and len(doi) == 1: doi = doi[0] elif not isinstance(doi, str): doi = 'No doi!' print('doi', doi) # TODO: deal with no doi situation prodigy_data = get_prodigy_json_data_by_doi(doi) result = { 'total': 1, 'progress': None, 'session_id': None, 'tasks': [prodigy_data] } return JSONResponse(result) for i, route in enumerate(prodigy_app.router.routes): if route.path == '/get_session_questions': route.endpoint = hacky_get_questions route.app = request_response(hacky_get_questions) break